Compare commits

..

1 Commits

Author SHA1 Message Date
Mitternacht822
582f21b0b1 fix(linux): force Qt6 modules to link from bundled lib via rpath 2026-02-09 21:31:16 +04:00
540 changed files with 19316 additions and 31798 deletions

View File

@@ -2,7 +2,7 @@
/client/3rd-prebuild
/client/android
/client/cmake
/client/core/utils/serialization
/client/core/serialization
/client/daemon
/client/fonts
/client/images

View File

@@ -17,7 +17,6 @@ jobs:
QIF_VERSION: 4.7
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -99,7 +98,6 @@ jobs:
BUILD_ARCH: 64
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -206,7 +204,6 @@ jobs:
CXX: c++
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -321,7 +318,6 @@ jobs:
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -399,7 +395,6 @@ jobs:
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -482,7 +477,6 @@ jobs:
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
@@ -551,7 +545,6 @@ jobs:
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}

View File

@@ -17,7 +17,6 @@ jobs:
QIF_VERSION: 4.5
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.15.4)
set(AMNEZIAVPN_VERSION 4.8.13.0)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
DESCRIPTION "AmneziaVPN"
@@ -12,7 +12,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 2120)
set(APP_ANDROID_VERSION_CODE 2106)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -61,9 +61,6 @@ if(WIN32 AND NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
set(CPACK_PACKAGE_VENDOR "AmneziaVPN")
set(CPACK_PACKAGE_VERSION ${AMNEZIAVPN_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AmneziaVPN client")
set(AMNEZIA_LICENSE_TXT "${CMAKE_BINARY_DIR}/LICENSE.txt")
configure_file("${CMAKE_SOURCE_DIR}/LICENSE" "${AMNEZIA_LICENSE_TXT}" COPYONLY)
set(CPACK_RESOURCE_FILE_LICENSE "${AMNEZIA_LICENSE_TXT}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "AmneziaVPN")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
set(CPACK_PACKAGE_EXECUTABLES "AmneziaVPN" "AmneziaVPN")

View File

@@ -179,7 +179,7 @@ You may face compiling issues in QT Creator after you've worked in Android Studi
## License
This project is licensed under the GNU General Public License v3.0 (see LICENSE) and also includes third-party components distributed under their own terms (see THIRD_PARTY_LICENSES.md).
GPL v3.0
## Donate

View File

@@ -1,149 +0,0 @@
# Third-Party Licenses
This project is licensed under the GNU General Public License v3.0.
This file lists third-party software components used by this repository.
Each component is distributed under its own license as linked below.
---
## QtKeychain
- Source: https://github.com/frankosterfeld/qtkeychain
- License: BSD License
- License Text: https://www.gnu.org/licenses/license-list.html#ModifiedBSD
---
## QSimpleCrypto
- Source: https://github.com/n1flh31mur/QSimpleCrypto
- License: Apache License 2.0
- License Text: https://github.com/n1flh31mur/QSimpleCrypto/blob/master/LICENSE
---
## SortFilterProxyModel
- Source: https://github.com/oKcerG/SortFilterProxyModel
- License: MIT License
- License Text: https://github.com/oKcerG/SortFilterProxyModel/blob/master/LICENSE
---
## QJsonStruct
- Source: https://github.com/Qv2ray/QJsonStruct
- License: MIT License
- License Text: https://github.com/Qv2ray/QJsonStruct/blob/master/LICENSE
---
## QR Code Generator (qrcodegen)
- Source: https://github.com/nayuki/QR-Code-generator
- License: MIT License
- License Text: https://www.nayuki.io/page/qr-code-generator-library
---
## Qt Gamepad
- Source: https://github.com/qt/qtgamepad
- License: GNU General Public License v3.0 (GPL-3.0)
- License Text: https://www.gnu.org/licenses/gpl-3.0.en.html
---
## AmneziaWG Apple (WireGuard)
- Source: https://github.com/amnezia-vpn/amneziawg-apple
- License: MIT License
- License Text: https://github.com/amnezia-vpn/amneziawg-apple/blob/master/COPYING
---
## AmneziaWG Android
- Source: https://github.com/amnezia-vpn/amneziawg-go
- License: MIT License
- License Text: https://github.com/amnezia-vpn/amneziawg-go/blob/master/LICENSE
---
## Xray Core
- Source: https://github.com/XTLS/Xray-core
- License: Mozilla Public License 2.0 (MPL-2.0)
- License Text: https://github.com/XTLS/Xray-core/blob/main/LICENSE
---
## Cloak
- Source: https://github.com/cbeuw/Cloak
- License: GNU General Public License v3.0 (GPL-3.0)
- License Text: https://github.com/cbeuw/Cloak/blob/master/LICENSE
---
## Shadowsocks
- Source: https://github.com/shadowsocks/shadowsocks-libev
- License: GPL-3.0-or-later
- License Text: http://www.gnu.org/licenses/
---
## OpenSSL
- Source: https://github.com/openssl/openssl
- License: Apache License 2.0
- License Text: https://www.openssl.org/source/license.html
---
## libssh
- Source: https://www.libssh.org/
- License: GNU Lesser General Public License (LGPL)
- License Text: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
---
## OpenVPNAdapter
- Source: https://github.com/ss-abramchuk/OpenVPNAdapter
- License: GNU Affero General Public License v3.0 (AGPL-3.0)
- License Text: https://github.com/ss-abramchuk/OpenVPNAdapter/blob/master/LICENSE
---
## Wintun
- Source: https://www.wintun.net/
- License: Prebuilt Binaries License
- License Text: https://github.com/WireGuard/wintun/blob/master/prebuilt-binaries-license.txt
---
## Mullvad Split Tunnel Driver
- Source: https://github.com/mullvad/win-split-tunnel
- License: GNU General Public License v3.0 (GPL-3.0) and Mozilla Public License Version 2.0
- License Text: https://github.com/mullvad/win-split-tunnel/blob/master/LICENSE-GPL.md https://github.com/mullvad/win-split-tunnel/blob/master/LICENSE-MPL.txt
---
## tun2socks
- Source: https://github.com/eycorsican/go-tun2socks
- License: MIT License
- License Text: https://github.com/eycorsican/go-tun2socks/blob/master/LICENSE
---
## TAP-Windows Driver
- Source: https://github.com/OpenVPN/tap-windows6
- License: tap-windows6 license
- License Text: https://github.com/OpenVPN/tap-windows6/blob/master/COPYING

View File

@@ -25,7 +25,6 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
add_definitions(-DFALLBACK_S3_ENDPOINT="$ENV{FALLBACK_S3_ENDPOINT}")
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
@@ -38,6 +37,10 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets)
endif()
if(LINUX AND NOT ANDROID)
list(APPEND PACKAGES QuickTemplates2 QmlModels OpenGL)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES})
set(LIBS ${LIBS}
@@ -53,6 +56,23 @@ endif()
qt_standard_project_setup()
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
if(LINUX AND NOT ANDROID)
target_link_options(${PROJECT} PRIVATE "-Wl,--no-as-needed")
target_link_options(${PROJECT} PRIVATE "LINKER:--disable-new-dtags")
set_target_properties(${PROJECT} PROPERTIES
BUILD_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH_USE_LINK_PATH FALSE
)
set_property(TARGET ${PROJECT} PROPERTY BUILD_WITH_INSTALL_RPATH TRUE)
target_link_libraries(${PROJECT} PRIVATE
Qt6::QuickTemplates2
Qt6::QmlModels
Qt6::OpenGL
)
endif()
target_include_directories(${PROJECT} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
@@ -60,14 +80,10 @@ target_include_directories(${PROJECT} PUBLIC
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
endif()
qt6_add_resources(QRC ${QRC}
${CMAKE_CURRENT_LIST_DIR}/images/images.qrc
${CMAKE_CURRENT_LIST_DIR}/images/flagKit.qrc
${CMAKE_CURRENT_LIST_DIR}/ui/qml/qml.qrc
${CMAKE_CURRENT_LIST_DIR}/server_scripts/serverScripts.qrc
)
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
# -- i18n begin
set(CMAKE_AUTORCC ON)
@@ -84,7 +100,6 @@ set(AMNEZIAVPN_TS_FILES
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
list(FILTER AMNEZIAVPN_TS_SOURCES EXCLUDE REGEX "qtgamepad/examples")
qt_create_translation(AMNEZIAVPN_QM_FILES ${AMNEZIAVPN_TS_SOURCES} ${AMNEZIAVPN_TS_FILES})
@@ -201,6 +216,17 @@ elseif(APPLE)
endif()
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
if(LINUX AND NOT ANDROID)
target_link_libraries(${PROJECT} PRIVATE
"-Wl,--push-state,--no-as-needed"
Qt6::QuickTemplates2
Qt6::QmlModels
Qt6::OpenGL
"-Wl,--pop-state"
)
endif()
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
# deploy artifacts required to run the application to the debug build folder
@@ -233,12 +259,6 @@ if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
)
endif()
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
add_subdirectory(tests)
endif()
list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.cpp)
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).

View File

@@ -1,4 +1,4 @@
#include "amneziaApplication.h"
#include "amnezia_application.h"
#include <QClipboard>
#include <QFontDatabase>
@@ -15,17 +15,17 @@
#include <QEvent>
#include <QDir>
#include <QSettings>
#include <QtQuick/QQuickWindow>
#include <QWindow>
#include "core/protocols/qmlRegisterProtocols.h"
#include "logger.h"
#include "ui/controllers/qml/pageController.h"
#include "ui/controllers/pageController.h"
#include "ui/models/installedAppsModel.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
#include "protocols/qml_register_protocols.h"
#include <QtQuick/QQuickWindow> // for QQuickWindow
#include <QWindow> // for qobject_cast<QWindow*>
bool AmneziaApplication::m_forceQuit = false;
@@ -54,7 +54,7 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif
m_settings = new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, this);
m_settings = std::shared_ptr<Settings>(new Settings);
m_nam = new QNetworkAccessManager(this);
}
@@ -109,16 +109,6 @@ void AmneziaApplication::init()
// install filter on main window
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
win->installEventFilter(this);
#ifdef Q_OS_ANDROID
QObject::connect(win, &QQuickWindow::sceneGraphError,
[](QQuickWindow::SceneGraphError, const QString &msg) {
qWarning() << "Scene graph error (suppressed):" << msg;
});
// Keep graphics context alive across hide/show cycles to avoid
// eglSwapBuffers/makeCurrent being called on a context Android has reclaimed.
win->setPersistentSceneGraph(true);
win->setPersistentGraphics(true);
#endif
win->show();
}
},
@@ -132,7 +122,7 @@ void AmneziaApplication::init()
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
#endif
m_vpnConnection.reset(new VpnConnection(nullptr, nullptr));
m_vpnConnection.reset(new VpnConnection(m_settings));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
@@ -153,6 +143,16 @@ void AmneziaApplication::init()
m_coreController->setQmlRoot();
bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID
if (enabled) {
if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#endif
Logger::setServiceLogsEnabled(enabled);
#ifdef Q_OS_WIN //TODO
if (m_parser.isSet(m_optAutostart))
m_coreController->pageController()->showOnStartup();
@@ -197,11 +197,13 @@ void AmneziaApplication::registerTypes()
qRegisterMetaType<ServerCredentials>("ServerCredentials");
qRegisterMetaType<DockerContainer>("DockerContainer");
using namespace amnezia::ProtocolEnumNS;
qRegisterMetaType<TransportProto>("TransportProto");
qRegisterMetaType<Proto>("Proto");
qRegisterMetaType<ServiceType>("ServiceType");
declareQmlProtocolEnum();
declareQmlContainerEnum();
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
m_containerProps.reset(new ContainerProps());
@@ -215,7 +217,6 @@ void AmneziaApplication::registerTypes()
qmlRegisterType<InstalledAppsModel>("InstalledAppsModel", 1, 0, "InstalledAppsModel");
amnezia::declareQmlProtocolEnum();
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
}

View File

@@ -14,10 +14,8 @@
#include <QClipboard>
#include "core/controllers/coreController.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
#include "ui/models/containerProps.h"
#include "ui/models/protocolProps.h"
#include "settings.h"
#include "vpnconnection.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
@@ -53,7 +51,7 @@ public slots:
private:
static bool m_forceQuit;
QQmlApplicationEngine *m_engine {};
SecureQSettings* m_settings;
std::shared_ptr<Settings> m_settings;
QScopedPointer<CoreController> m_coreController;

View File

@@ -111,6 +111,7 @@ dependencies {
implementation(project(":wireguard"))
implementation(project(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(project(":xray"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.cloak"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":openvpn"))
}

View File

@@ -0,0 +1,45 @@
package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
import org.json.JSONObject
class Cloak : OpenVpn() {
override fun internalInit() {
super.internalInit()
if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
}
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
openVpnConfig.usePluggableTransports = true
openVpnConfig.content = configStr
return openVpnConfig
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
cloakConfigJson.put("NumConn", 1)
cloakConfigJson.put("ProxyMethod", "openvpn")
if (cloakConfigJson.has("port")) {
val port = cloakConfigJson["port"]
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", port)
}
if (cloakConfigJson.has("remote")) {
val remote = cloakConfigJson["remote"]
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", remote)
}
return cloakConfigJson
}
}

View File

@@ -35,6 +35,7 @@ include(":protocolApi")
include(":wireguard")
include(":awg")
include(":openvpn")
include(":cloak")
include(":xray")
include(":xray:libXray")

View File

@@ -75,8 +75,6 @@ private const val OPEN_FILE_ACTION_CODE = 3
private const val CHECK_NOTIFICATION_PERMISSION_ACTION_CODE = 4
private const val PREFS_NOTIFICATION_PERMISSION_ASKED = "NOTIFICATION_PERMISSION_ASKED"
private const val OPEN_FILE_AFTER_RESUME_DELAY_MS = 400L
private const val KEY_PENDING_OPEN_FILE_URI = "pending_open_file_uri"
class AmneziaActivity : QtActivity() {
@@ -93,12 +91,6 @@ class AmneziaActivity : QtActivity() {
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
private var isActivityResumed = false
private var hasWindowFocus = false
private val resumeHandler = Handler(Looper.getMainLooper())
private var pendingOpenFileUri: String? = null
private var openFileDeliveryScheduled = false
private val vpnServiceEventHandler: Handler by lazy(NONE) {
object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
@@ -200,18 +192,11 @@ class AmneziaActivity : QtActivity() {
doBindService()
}
)
pendingOpenFileUri = savedInstanceState?.getString(KEY_PENDING_OPEN_FILE_URI)
openFileDeliveryScheduled = false
registerBroadcastReceivers()
intent?.let(::processIntent)
runBlocking { vpnProto = proto.await() }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
pendingOpenFileUri?.let { outState.putString(KEY_PENDING_OPEN_FILE_URI, it) }
}
private fun loadLibs() {
listOf(
"rsapss",
@@ -277,11 +262,6 @@ class AmneziaActivity : QtActivity() {
}
override fun onStop() {
isActivityResumed = false
hasWindowFocus = false
// Cancel all pending operations when activity stops
resumeHandler.removeCallbacksAndMessages(null)
openFileDeliveryScheduled = false
Log.d(TAG, "Stop Amnezia activity")
doUnbindService()
mainScope.launch {
@@ -293,57 +273,39 @@ class AmneziaActivity : QtActivity() {
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
hasWindowFocus = hasFocus
Log.d(TAG, "Window focus changed: hasFocus=$hasFocus")
if (!hasFocus) {
// Cancel pending operations if window loses focus
resumeHandler.removeCallbacksAndMessages(null)
} else if (isActivityResumed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
window.decorView.apply {
invalidate()
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(1f, 1f)
}
}, 50)
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(2f, 2f)
requestLayout()
invalidate()
}
}, 150)
}
}
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val deviceId = event.deviceId
val keyCode = event.keyCode
val pressed = event.action == KeyEvent.ACTION_DOWN
val source = event.source
when (keyCode) {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_SELECT -> {
nativeGamepadKeyEvent(0, keyCode, pressed)
if (deviceId < 0 && pressed) {
when (keyCode) {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER -> {
nativeGamepadKeyEvent(0, keyCode, true)
nativeGamepadKeyEvent(0, keyCode, false)
return true
}
}
KeyEvent.KEYCODE_DPAD_CENTER,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT -> {
val syntheticKeyCode = if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) KeyEvent.KEYCODE_ENTER else keyCode
val synthetic = KeyEvent(
event.downTime, event.eventTime, event.action, syntheticKeyCode,
event.repeatCount, event.metaState, -1, event.scanCode,
event.flags, InputDevice.SOURCE_KEYBOARD
)
return super.dispatchKeyEvent(synthetic)
}
// Real gamepad events (deviceId >= 0)
if (deviceId >= 0) {
val isGamepad = (source and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
val isJoystick = (source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
val isDpad = (source and InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
if (isGamepad || isJoystick || isDpad) {
nativeGamepadKeyEvent(deviceId, keyCode, pressed)
return true
}
}
@@ -353,69 +315,31 @@ class AmneziaActivity : QtActivity() {
private external fun nativeGamepadKeyEvent(deviceId: Int, keyCode: Int, pressed: Boolean)
override fun onPause() {
// Notify Qt to stop rendering BEFORE super.onPause() destroys the EGL surface.
// Using a coroutine here would be too late — the surface is gone by the time
// the coroutine runs. A direct synchronous call gives Qt's render thread the
// best chance to process visible=false before surface destruction.
if (qtInitialized.isCompleted) {
QtAndroidController.onActivityPaused()
}
super.onPause()
isActivityResumed = false
// Cancel all pending operations when activity pauses
resumeHandler.removeCallbacksAndMessages(null)
openFileDeliveryScheduled = false
Log.d(TAG, "Pause Amnezia activity")
}
override fun onResume() {
super.onResume()
isActivityResumed = true
Log.d(TAG, "Resume Amnezia activity")
if (qtInitialized.isCompleted) {
QtAndroidController.onActivityResumed()
}
if (pendingOpenFileUri != null && !openFileDeliveryScheduled) {
val uri = pendingOpenFileUri!!
openFileDeliveryScheduled = true
resumeHandler.postDelayed({
if (!isFinishing && !isDestroyed) {
pendingOpenFileUri = null
openFileDeliveryScheduled = false
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
}, OPEN_FILE_AFTER_RESUME_DELAY_MS)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
window.decorView.apply {
invalidate()
resumeHandler.postDelayed({
// Check if activity is still resumed and has focus before executing
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(1f, 1f)
}
postDelayed({
sendTouch(1f, 1f)
}, 100)
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(2f, 2f)
}
postDelayed({
sendTouch(2f, 2f)
}, 200)
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
requestLayout()
invalidate()
}
postDelayed({
requestLayout()
invalidate()
}, 250)
}
}
} */
Log.d(TAG, "Resume Amnezia activity")
}
private fun configureWindowForEdgeToEdge() {
@@ -453,35 +377,31 @@ class AmneziaActivity : QtActivity() {
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, windowInsets ->
val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = if (imeVisible) imeInsets.bottom else 0
val density = resources.displayMetrics.density
val imeHeightDp = (imeHeight / density).toInt()
// Also track system bars (navigation bar, status bar) changes
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val navBarHeight = systemBarsInsets.bottom
val navBarHeightDp = (navBarHeight / density).toInt()
val statusBarHeight = systemBarsInsets.top
val statusBarHeightDp = (statusBarHeight / density).toInt()
mainScope.launch {
qtInitialized.await()
QtAndroidController.onImeInsetsChanged(imeHeightDp)
QtAndroidController.onSystemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp)
}
// Return windowInsets instead of CONSUMED to allow proper handling
windowInsets
}
}
override fun onDestroy() {
isActivityResumed = false
hasWindowFocus = false
// Cancel all pending operations when activity is destroyed
resumeHandler.removeCallbacksAndMessages(null)
Log.d(TAG, "Destroy Amnezia activity")
unregisterBroadcastReceiver(notificationStateReceiver)
notificationStateReceiver = null
@@ -807,13 +727,9 @@ class AmneziaActivity : QtActivity() {
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}?.toString() ?: ""
Log.v(TAG, "Open file: $uri")
if (uri.isNotEmpty()) {
pendingOpenFileUri = uri
} else {
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
))
@@ -842,7 +758,7 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused")
fun getFd(fileName: String): Int {
Log.v(TAG, "Get fd for $fileName")
return blockingCall(Dispatchers.IO) {
return blockingCall {
try {
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
pfd?.fd ?: -1

View File

@@ -33,10 +33,7 @@ class TvFilePicker : ComponentActivity() {
return intent
}
}) {
setResult(RESULT_OK, Intent().apply {
data = it
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
})
setResult(RESULT_OK, Intent().apply { data = it })
finish()
}

View File

@@ -2,6 +2,7 @@ package org.amnezia.vpn
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.awg.Awg
import org.amnezia.vpn.protocol.cloak.Cloak
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.protocol.xray.Xray
@@ -35,6 +36,14 @@ enum class VpnProto(
override fun createProtocol(): Protocol = OpenVpn()
},
CLOAK(
"Cloak",
"org.amnezia.vpn:amneziaOpenVpnService",
OpenVpnService::class.java
) {
override fun createProtocol(): Protocol = Cloak()
},
XRAY(
"XRay",
"org.amnezia.vpn:amneziaXrayService",
@@ -63,4 +72,4 @@ enum class VpnProto(
companion object {
fun get(protocolName: String): VpnProto = VpnProto.valueOf(protocolName.uppercase())
}
}
}

View File

@@ -31,7 +31,4 @@ object QtAndroidController {
external fun onImeInsetsChanged(heightDp: Int)
external fun onSystemBarsInsetsChanged(navBarHeightDp: Int, statusBarHeightDp: Int)
external fun onActivityPaused()
external fun onActivityResumed()
}

View File

@@ -4,9 +4,6 @@ import android.content.Context
import android.net.VpnService.Builder
import java.io.File
import java.io.IOException
import java.net.InetAddress
import java.net.ServerSocket
import java.util.UUID
import go.Seq
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
@@ -22,32 +19,11 @@ import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.ip
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONArray
import org.json.JSONObject
private const val TAG = "Xray"
private const val LIBXRAY_TAG = "libXray"
private fun findSocksInboundIndex(inbounds: JSONArray): Int {
for (i in 0 until inbounds.length()) {
val o = inbounds.optJSONObject(i) ?: continue
if (o.optString("protocol").equals("socks", ignoreCase = true)) {
return i
}
}
return -1
}
private fun acquireFreeLocalPort(): Int {
try {
ServerSocket(0, 1, InetAddress.getByName("127.0.0.1")).use { return it.localPort }
} catch (e: Exception) {
throw VpnStartException(
"Failed to acquire free TCP port on 127.0.0.1 for SOCKS inbound: ${e.message}"
)
}
}
class Xray : Protocol() {
private var isRunning: Boolean = false
@@ -77,13 +53,9 @@ class Xray : Protocol() {
return
}
val xrayConfigData = config.optJSONObject("xray_config_data")
val xrayJsonConfig = config.optJSONObject("xray_config_data")
?: config.optJSONObject("ssxray_config_data")
?: throw BadConfigException("config_data not found")
val xrayJsonConfig = JSONObject(xrayConfigData.optString("config"))
// Inject SOCKS5 auth before starting xray. Re-uses existing credentials if present.
ensureInboundAuth(xrayJsonConfig)
val xrayConfig = parseConfig(config, xrayJsonConfig)
(xrayJsonConfig.optJSONObject("log") ?: JSONObject().also { xrayJsonConfig.put("log", it) })
@@ -125,22 +97,9 @@ class Xray : Protocol() {
if (it.isNotBlank()) setMtu(it.toInt())
}
val inbounds = xrayJsonConfig.getJSONArray("inbounds")
val socksIdx = findSocksInboundIndex(inbounds)
if (socksIdx < 0) {
throw BadConfigException("socks inbound not found")
}
val socksConfig = inbounds.getJSONObject(socksIdx)
val socksConfig = xrayJsonConfig.getJSONArray("inbounds")[0] as JSONObject
socksConfig.getInt("port").let { setSocksPort(it) }
val socksSettings = socksConfig.optJSONObject("settings")
val accounts = socksSettings?.optJSONArray("accounts")
if (accounts != null && accounts.length() > 0) {
val account = accounts.getJSONObject(0)
setSocksUser(account.optString("user"))
setSocksPass(account.optString("pass"))
}
configSplitTunneling(config)
configAppSplitTunneling(config)
}
@@ -203,10 +162,9 @@ class Xray : Protocol() {
}
private fun runTun2Socks(config: XrayConfig, fd: Int) {
val proxyUrl = "socks5://${config.socksUser}:${config.socksPass}@127.0.0.1:${config.socksPort}"
val tun2SocksConfig = Tun2SocksConfig().apply {
mtu = config.mtu.toLong()
proxy = proxyUrl
proxy = "socks5://127.0.0.1:${config.socksPort}"
device = "fd://$fd"
logLevel = "warn"
}
@@ -215,37 +173,6 @@ class Xray : Protocol() {
}
}
// Ensures SOCKS5 auth is present on the socks inbound settings.
// Re-uses existing credentials if already configured; otherwise generates random ones.
private fun ensureInboundAuth(xrayConfig: JSONObject) {
val inbounds = xrayConfig.optJSONArray("inbounds") ?: return
val socksIdx = findSocksInboundIndex(inbounds)
if (socksIdx < 0) return
val inbound = inbounds.getJSONObject(socksIdx)
inbound.put("port", acquireFreeLocalPort())
val settings = inbound.optJSONObject("settings") ?: JSONObject().also { inbound.put("settings", it) }
val accounts = settings.optJSONArray("accounts")
if (accounts != null && accounts.length() > 0) {
val account = accounts.getJSONObject(0)
if (account.optString("user").isNotEmpty() && account.optString("pass").isNotEmpty()) {
// Ensure auth mode is enforced even for imported configs that had accounts
// but auth: "noauth" (or no auth field).
settings.put("auth", "password")
inbound.put("settings", settings)
inbounds.put(socksIdx, inbound)
return
}
}
val user = UUID.randomUUID().toString().replace("-", "").substring(0, 16)
val pass = UUID.randomUUID().toString().replace("-", "")
settings.put("auth", "password")
settings.put("accounts", JSONArray().put(JSONObject().put("user", user).put("pass", pass)))
inbound.put("settings", settings)
inbounds.put(socksIdx, inbound)
}
companion object {
val instance: Xray by lazy { Xray() }
}

View File

@@ -9,16 +9,12 @@ private const val XRAY_DEFAULT_MAX_MEMORY: Long = 50 shl 20 // 50 MB
class XrayConfig protected constructor(
protocolConfigBuilder: ProtocolConfig.Builder,
val socksPort: Int,
val socksUser: String,
val socksPass: String,
val maxMemory: Long,
) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this(
builder,
builder.socksPort,
builder.socksUser,
builder.socksPass,
builder.maxMemory
)
@@ -26,12 +22,6 @@ class XrayConfig protected constructor(
internal var socksPort: Int = 0
private set
internal var socksUser: String = ""
private set
internal var socksPass: String = ""
private set
internal var maxMemory: Long = XRAY_DEFAULT_MAX_MEMORY
private set
@@ -39,10 +29,6 @@ class XrayConfig protected constructor(
fun setSocksPort(port: Int) = apply { socksPort = port }
fun setSocksUser(user: String) = apply { socksUser = user }
fun setSocksPass(pass: String) = apply { socksPass = pass }
fun setMaxMemory(maxMemory: Long) = apply { this.maxMemory = maxMemory }
override fun build(): XrayConfig = configBuild().run { XrayConfig(this@Builder) }

View File

@@ -31,15 +31,15 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/core/protocols/androidVpnProtocol.h
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/protocols/androidVpnProtocol.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
)
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})

View File

@@ -121,7 +121,6 @@ target_sources(${PROJECT} PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
${CLIENT_ROOT_DIR}/platforms/ios/StoreKit2Helper.swift
)
target_sources(${PROJECT} PRIVATE

View File

@@ -28,11 +28,11 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.h
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.mm
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
)

View File

@@ -131,7 +131,6 @@ target_sources(${PROJECT} PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
${CLIENT_ROOT_DIR}/platforms/ios/StoreKit2Helper.swift
)
target_sources(${PROJECT} PRIVATE
@@ -164,7 +163,7 @@ add_custom_command(TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
COMMAND /usr/bin/codesign --force --sign "Apple Distribution: Privacy Technologies OU"
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Signing OpenVPNAdapter framework"

View File

@@ -1,68 +1,34 @@
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/migrations.h
${CLIENT_ROOT_DIR}/migrations.h
${CLIENT_ROOT_DIR}/../ipc/ipc.h
${CLIENT_ROOT_DIR}/amneziaApplication.h
${CLIENT_ROOT_DIR}/core/utils/errorCodes.h
${CLIENT_ROOT_DIR}/core/utils/routeModes.h
${CLIENT_ROOT_DIR}/core/utils/commonStructs.h
${CLIENT_ROOT_DIR}/core/utils/containerEnum.h
${CLIENT_ROOT_DIR}/core/utils/protocolEnum.h
${CLIENT_ROOT_DIR}/core/utils/containers/containerUtils.h
${CLIENT_ROOT_DIR}/core/protocols/protocolUtils.h
${CLIENT_ROOT_DIR}/core/utils/constants/configKeys.h
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h
${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h
${CLIENT_ROOT_DIR}/core/utils/api/apiEnums.h
${CLIENT_ROOT_DIR}/core/utils/errorStrings.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
${CLIENT_ROOT_DIR}/amnezia_application.h
${CLIENT_ROOT_DIR}/containers/containers_defs.h
${CLIENT_ROOT_DIR}/core/defs.h
${CLIENT_ROOT_DIR}/core/errorstrings.h
${CLIENT_ROOT_DIR}/core/scripts_registry.h
${CLIENT_ROOT_DIR}/core/server_defs.h
${CLIENT_ROOT_DIR}/core/api/apiDefs.h
${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
${CLIENT_ROOT_DIR}/core/controllers/coreController.h
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.h
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.h
${CLIENT_ROOT_DIR}/core/controllers/serversController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/importController.h
${CLIENT_ROOT_DIR}/core/installers/installerBase.h
${CLIENT_ROOT_DIR}/core/installers/awgInstaller.h
${CLIENT_ROOT_DIR}/core/installers/wireguardInstaller.h
${CLIENT_ROOT_DIR}/core/installers/openvpnInstaller.h
${CLIENT_ROOT_DIR}/core/installers/xrayInstaller.h
${CLIENT_ROOT_DIR}/core/installers/torInstaller.h
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.h
${CLIENT_ROOT_DIR}/core/controllers/connectionController.h
${CLIENT_ROOT_DIR}/core/controllers/settingsController.h
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.h
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.h
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.h
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.h
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.h
${CLIENT_ROOT_DIR}/core/protocols/qmlRegisterProtocols.h
${CLIENT_ROOT_DIR}/ui/utils/pages.h
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.h
${CLIENT_ROOT_DIR}/core/protocols/vpnProtocol.h
${CLIENT_ROOT_DIR}/core/controllers/serverController.h
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
${CLIENT_ROOT_DIR}/ui/pages.h
${CLIENT_ROOT_DIR}/ui/qautostart.h
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h
${CMAKE_CURRENT_BINARY_DIR}/version.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshClient.h
${CLIENT_ROOT_DIR}/core/utils/networkUtilities.h
${CLIENT_ROOT_DIR}/core/utils/serialization/serialization.h
${CLIENT_ROOT_DIR}/core/utils/serialization/transfer.h
${CLIENT_ROOT_DIR}/core/sshclient.h
${CLIENT_ROOT_DIR}/core/networkUtilities.h
${CLIENT_ROOT_DIR}/core/serialization/serialization.h
${CLIENT_ROOT_DIR}/core/serialization/transfer.h
${CLIENT_ROOT_DIR}/../common/logger/logger.h
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.h
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.h
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.h
${CLIENT_ROOT_DIR}/core/utils/utilities.h
${CLIENT_ROOT_DIR}/core/utils/managementServer.h
${CLIENT_ROOT_DIR}/core/utils/constants.h
${CLIENT_ROOT_DIR}/utils/qmlUtils.h
${CLIENT_ROOT_DIR}/core/api/apiUtils.h
${CLIENT_ROOT_DIR}/core/osSignalHandler.h
)
# Mozilla headres
@@ -81,64 +47,39 @@ endif()
if(NOT ANDROID)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.h
${CLIENT_ROOT_DIR}/ui/notificationhandler.h
)
endif()
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/utils/migrations.cpp
${CLIENT_ROOT_DIR}/amneziaApplication.cpp
${CLIENT_ROOT_DIR}/core/utils/errorStrings.cpp
${CLIENT_ROOT_DIR}/core/utils/containers/containerUtils.cpp
${CLIENT_ROOT_DIR}/core/protocols/protocolUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.cpp
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.cpp
${CLIENT_ROOT_DIR}/migrations.cpp
${CLIENT_ROOT_DIR}/amnezia_application.cpp
${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
${CLIENT_ROOT_DIR}/core/errorstrings.cpp
${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
${CLIENT_ROOT_DIR}/core/server_defs.cpp
${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.cpp
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.cpp
${CLIENT_ROOT_DIR}/core/controllers/serversController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/importController.cpp
${CLIENT_ROOT_DIR}/core/installers/installerBase.cpp
${CLIENT_ROOT_DIR}/core/installers/awgInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/wireguardInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/openvpnInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/xrayInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.cpp
${CLIENT_ROOT_DIR}/core/controllers/connectionController.cpp
${CLIENT_ROOT_DIR}/core/controllers/settingsController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.cpp
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.cpp
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.cpp
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.cpp
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.cpp
${CLIENT_ROOT_DIR}/core/protocols/vpnProtocol.cpp
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshClient.cpp
${CLIENT_ROOT_DIR}/core/utils/networkUtilities.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/outbound.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/inbound.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/ss.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/ssd.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/vless.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/trojan.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess.cpp
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess_new.cpp
${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
${CLIENT_ROOT_DIR}/ui/qautostart.cpp
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp
${CLIENT_ROOT_DIR}/core/sshclient.cpp
${CLIENT_ROOT_DIR}/core/networkUtilities.cpp
${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp
${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp
${CLIENT_ROOT_DIR}/core/serialization/ss.cpp
${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp
${CLIENT_ROOT_DIR}/core/serialization/vless.cpp
${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp
${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp
${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
${CLIENT_ROOT_DIR}/core/osSignalHandler.cpp
)
# Mozilla sources
@@ -159,41 +100,29 @@ if(APPLE AND NOT IOS)
list(APPEND HEADERS
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
${CLIENT_ROOT_DIR}/ui/utils/macosUtil.h
${CLIENT_ROOT_DIR}/ui/macos_util.h
)
list(APPEND SOURCES
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
${CLIENT_ROOT_DIR}/ui/utils/macosUtil.mm
${CLIENT_ROOT_DIR}/ui/macos_util.mm
)
endif()
if(NOT ANDROID)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.cpp
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
)
endif()
set(COMMON_FILES_H
${CLIENT_ROOT_DIR}/amneziaApplication.h
${CLIENT_ROOT_DIR}/secureQSettings.h
${CLIENT_ROOT_DIR}/vpnConnection.h
)
set(COMMON_FILES_CPP
${CLIENT_ROOT_DIR}/amneziaApplication.cpp
${CLIENT_ROOT_DIR}/secureQSettings.cpp
${CLIENT_ROOT_DIR}/vpnConnection.cpp
)
file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h)
file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp)
file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/configurators/*.h)
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/configurators/*.cpp)
file(GLOB_RECURSE CORE_MODELS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/models/*.h)
file(GLOB_RECURSE CORE_MODELS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/models/*.cpp)
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h)
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp)
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/models/*.h
@@ -211,21 +140,16 @@ file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/controllers/*.h
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
${CLIENT_ROOT_DIR}/ui/controllers/qml/*.h
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.h
)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
${CLIENT_ROOT_DIR}/ui/controllers/qml/*.cpp
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.cpp
)
set(HEADERS ${HEADERS}
${COMMON_FILES_H}
${PAGE_LOGIC_H}
${CONFIGURATORS_H}
${CORE_MODELS_H}
${UI_MODELS_H}
${UI_CONTROLLERS_H}
)
@@ -233,18 +157,17 @@ set(SOURCES ${SOURCES}
${COMMON_FILES_CPP}
${PAGE_LOGIC_CPP}
${CONFIGURATORS_CPP}
${CORE_MODELS_CPP}
${UI_MODELS_CPP}
${UI_CONTROLLERS_CPP}
)
if(WIN32)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/protocols/ikev2VpnProtocolWindows.h
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/protocols/ikev2VpnProtocolWindows.cpp
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
)
set(RESOURCES ${RESOURCES}
@@ -257,33 +180,39 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
add_compile_definitions(AMNEZIA_DESKTOP)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/ipcClient.h
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.h
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.h
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.h
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.h
${CLIENT_ROOT_DIR}/core/ipcclient.h
${CLIENT_ROOT_DIR}/core/privileged_process.h
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/utils/ipcClient.cpp
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
${CLIENT_ROOT_DIR}/core/privileged_process.cpp
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.cpp
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.cpp
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.cpp
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.cpp
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp
)
endif()
if(APPLE AND MACOS_NE)
# Include only the tray notification handler in NE builds
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
)
endif()

View File

@@ -0,0 +1,59 @@
#include "awg_configurator.h"
#include "protocols/protocols_defs.h"
#include <QJsonDocument>
#include <QJsonObject>
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: WireguardConfigurator(settings, serverController, true, parent)
{
}
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
QString awgConfig = jsonConfig.value(config_key::config).toString();
QMap<QString, QString> configMap;
auto configLines = awgConfig.split("\n");
for (auto &line : configLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
if (container == DockerContainer::Awg2) {
jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
}
jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
jsonConfig[config_key::mtu] =
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
return QJsonDocument(jsonConfig).toJson();
}

View File

@@ -0,0 +1,18 @@
#ifndef AWGCONFIGURATOR_H
#define AWGCONFIGURATOR_H
#include <QObject>
#include "wireguard_configurator.h"
class AwgConfigurator : public WireguardConfigurator
{
Q_OBJECT
public:
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // AWGCONFIGURATOR_H

View File

@@ -0,0 +1,51 @@
#include "cloak_configurator.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
QString cloakPublicKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
cloakPublicKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return "";
}
QString cloakBypassUid =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckBypassUidKeyPath, errorCode);
cloakBypassUid.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return "";
}
QJsonObject config;
config.insert("Transport", "direct");
config.insert("ProxyMethod", "openvpn");
config.insert("EncryptionMethod", "aes-gcm");
config.insert("UID", cloakBypassUid);
config.insert("PublicKey", cloakPublicKey);
config.insert("ServerName", "$FAKE_WEB_SITE_ADDRESS");
config.insert("NumConn", 1);
config.insert("BrowserSig", "chrome");
config.insert("StreamTimeout", 300);
config.insert("RemoteHost", credentials.hostName);
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
return textCfg;
}

View File

@@ -0,0 +1,20 @@
#ifndef CLOAK_CONFIGURATOR_H
#define CLOAK_CONFIGURATOR_H
#include <QObject>
#include "configurator_base.h"
using namespace amnezia;
class CloakConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // CLOAK_CONFIGURATOR_H

View File

@@ -0,0 +1,26 @@
#include "configurator_base.h"
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: QObject { parent }, m_settings(settings), m_serverController(serverController)
{
}
QString ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
QString ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString)
{
protocolConfigString.replace("$PRIMARY_DNS", dns.first);
protocolConfigString.replace("$SECONDARY_DNS", dns.second);
}

View File

@@ -0,0 +1,33 @@
#ifndef CONFIGURATORBASE_H
#define CONFIGURATORBASE_H
#include <QObject>
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/controllers/serverController.h"
#include "settings.h"
class ConfiguratorBase : public QObject
{
Q_OBJECT
public:
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0;
virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
virtual QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
protected:
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;
};
#endif // CONFIGURATORBASE_H

View File

@@ -1,4 +1,4 @@
#include "ikev2Configurator.h"
#include "ikev2_configurator.h"
#include <QDebug>
#include <QJsonDocument>
@@ -8,16 +8,14 @@
#include <QTemporaryFile>
#include <QUuid>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
#include "core/utils/utilities.h"
#include "core/models/protocols/ikev2ProtocolConfig.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "utilities.h"
Ikev2Configurator::Ikev2Configurator(SshSession* sshSession, QObject *parent)
: ConfiguratorBase(sshSession, parent)
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
@@ -27,6 +25,7 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
Ikev2Configurator::ConnectionData connData;
connData.host = credentials.hostName;
connData.clientId = Utils::getRandomString(16);
connData.password = Utils::getRandomString(16);
connData.password = "";
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
@@ -40,14 +39,14 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
.arg(connData.clientId);
errorCode = m_sshSession->runContainerScript(credentials, container, scriptCreateCert);
errorCode = m_serverController->runContainerScript(credentials, container, scriptCreateCert);
QString scriptExportCert =
QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"").arg(connData.password).arg(connData.clientId).arg(certFileName);
errorCode = m_sshSession->runContainerScript(credentials, container, scriptExportCert);
errorCode = m_serverController->runContainerScript(credentials, container, scriptExportCert);
connData.clientCert = m_sshSession->getTextFileFromContainer(container, credentials, certFileName, errorCode);
connData.caCert = m_sshSession->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode);
connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, certFileName, errorCode);
connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode);
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();
@@ -55,51 +54,26 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
return connData;
}
ProtocolConfig Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
const Ikev2ServerConfig* serverConfig = nullptr;
if (auto* ikev2Config = containerConfig.protocolConfig.as<Ikev2ProtocolConfig>()) {
serverConfig = &ikev2Config->serverConfig;
}
Q_UNUSED(containerConfig)
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return Ikev2ProtocolConfig{};
return "";
}
QString configJson = genIkev2Config(connData);
QJsonDocument doc = QJsonDocument::fromJson(configJson.toUtf8());
QJsonObject configObj = doc.object();
Ikev2ProtocolConfig protocolConfig;
if (serverConfig) {
protocolConfig.serverConfig = *serverConfig;
} else {
protocolConfig.serverConfig.hostName = connData.host;
}
Ikev2ClientConfig clientConfig;
clientConfig.nativeConfig = configJson;
clientConfig.hostName = connData.host;
clientConfig.userName = connData.clientId;
clientConfig.cert = QString(connData.clientCert.toBase64());
clientConfig.password = connData.password;
clientConfig.clientId = connData.clientId;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
return genIkev2Config(connData);
}
QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData)
{
QJsonObject config;
config[configKey::hostName] = connData.host;
config[configKey::userName] = connData.clientId;
config[configKey::cert] = QString(connData.clientCert.toBase64());
config[configKey::password] = connData.password;
config[config_key::hostName] = connData.host;
config[config_key::userName] = connData.clientId;
config[config_key::cert] = QString(connData.clientCert.toBase64());
config[config_key::password] = connData.password;
return QJsonDocument(config).toJson();
}

View File

@@ -0,0 +1,35 @@
#ifndef IKEV2_CONFIGURATOR_H
#define IKEV2_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/defs.h"
class Ikev2Configurator : public ConfiguratorBase
{
Q_OBJECT
public:
Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
struct ConnectionData {
QByteArray clientCert; // p12 client cert
QByteArray caCert; // p12 server cert
QString clientId;
QString password; // certificate password
QString host; // host ip
};
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData);
ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode);
};
#endif // IKEV2_CONFIGURATOR_H

View File

@@ -1,9 +1,8 @@
#include "openVpnConfigurator.h"
#include "openvpn_configurator.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRegularExpression>
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
@@ -14,34 +13,26 @@
#include <QApplication>
#endif
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
#include "core/utils/utilities.h"
#include "core/models/protocols/openVpnProtocolConfig.h"
using namespace amnezia;
#include "core/networkUtilities.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "settings.h"
#include "utilities.h"
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
OpenVpnConfigurator::OpenVpnConfigurator(SshSession* sshSession, QObject *parent)
: ConfiguratorBase(sshSession, parent)
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
DockerContainer container, ErrorCode &errorCode)
{
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
connData.host = credentials.hostName;
@@ -53,26 +44,26 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath).arg(connData.clientId);
errorCode = m_sshSession->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
if (errorCode != ErrorCode::NoError) {
return connData;
}
errorCode = signCert(container, credentials, dnsSettings, connData.clientId);
errorCode = signCert(container, credentials, connData.clientId);
if (errorCode != ErrorCode::NoError) {
return connData;
}
connData.caCert =
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode);
connData.clientCert = m_sshSession->getTextFileFromContainer(
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode);
connData.clientCert = m_serverController->getTextFileFromContainer(
container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode);
if (errorCode != ErrorCode::NoError) {
return connData;
}
connData.taKey = m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
errorCode = ErrorCode::SshScpFailureError;
@@ -81,23 +72,15 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
ProtocolConfig OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
const OpenVpnServerConfig* serverConfig = nullptr;
if (auto* openVpnProtocolConfig = containerConfig.getOpenVpnProtocolConfig()) {
serverConfig = &openVpnProtocolConfig->serverConfig;
}
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
QString config = m_sshSession->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), vars);
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, dnsSettings, errorCode);
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return OpenVpnProtocolConfig{};
return "";
}
auto sanitizeStaticKey = [](const QString &key) {
@@ -133,45 +116,42 @@ ProtocolConfig OpenVpnConfigurator::createConfig(const ServerCredentials &creden
config.replace("block-outside-dns", "");
#endif
OpenVpnProtocolConfig protocolConfig;
if (serverConfig) {
protocolConfig.serverConfig = *serverConfig;
}
OpenVpnClientConfig clientConfig;
clientConfig.nativeConfig = config;
clientConfig.clientId = connData.clientId;
clientConfig.blockOutsideDns = false;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
QJsonObject jConfig;
jConfig[config_key::config] = config;
jConfig[config_key::clientId] = connData.clientId;
return QJsonDocument(jConfig).toJson();
}
ProtocolConfig OpenVpnConfigurator::processConfigWithLocalSettings(const ConnectionSettings &settings,
ProtocolConfig protocolConfig)
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
applyDnsToNativeConfig(settings.dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
QString config = protocolConfig.nativeConfig();
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
if (!settings.isApiConfig) {
if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (settings.dns.primaryDns.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + settings.dns.secondaryDns);
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
if (!settings.splitTunneling.isSitesSplitTunnelingEnabled) {
if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
config.append("block-ipv6\n");
} else if (settings.splitTunneling.routeMode == RouteMode::VpnOnlyForwardSites) {
// no redirect-gateway
} else if (settings.splitTunneling.routeMode == RouteMode::VpnAllExceptSites) {
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak
#endif
config.append("block-ipv6\n");
}
@@ -182,57 +162,64 @@ ProtocolConfig OpenVpnConfigurator::processConfigWithLocalSettings(const Connect
#endif
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
config.append(QString("\nscript-security 2\n"
"up %1/update-resolv-conf.sh\n"
"down %1/update-resolv-conf.sh\n")
.arg(qApp->applicationDirPath()));
QString dnsConf = QString("\nscript-security 2\n"
"up %1/update-resolv-conf.sh\n"
"down %1/update-resolv-conf.sh\n")
.arg(qApp->applicationDirPath());
config.append(dnsConf);
#endif
protocolConfig.setNativeConfig(config);
return protocolConfig;
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
ProtocolConfig OpenVpnConfigurator::processConfigWithExportSettings(const ExportSettings &settings,
ProtocolConfig protocolConfig)
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
applyDnsToNativeConfig(settings.dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
QString config = protocolConfig.nativeConfig();
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (settings.dns.primaryDns.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + settings.dns.secondaryDns);
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
config.append("block-ipv6\n");
// remove block-outside-dns for all exported configs
config.replace("block-outside-dns", "");
protocolConfig.setNativeConfig(config);
return protocolConfig;
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials,
const DnsSettings &dnsSettings, QString clientId)
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
{
QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && "
"easyrsa import-req %2/%3.req %3\"")
.arg(ContainerUtils::containerToString(container))
.arg(ContainerProps::containerToString(container))
.arg(amnezia::protocols::openvpn::clientsDirPath)
.arg(clientId);
QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && "
"easyrsa sign-req client %2\"")
.arg(ContainerUtils::containerToString(container))
.arg(ContainerProps::containerToString(container))
.arg(clientId);
QStringList scriptList { script_import, script_sign };
QString script = m_sshSession->replaceVars(scriptList.join("\n"), amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns));
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
return m_sshSession->runScript(credentials, script);
return m_serverController->runScript(credentials, script);
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()

View File

@@ -0,0 +1,43 @@
#ifndef OPENVPN_CONFIGURATOR_H
#define OPENVPN_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/defs.h"
class OpenVpnConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
struct ConnectionData
{
QString clientId;
QString request; // certificate request
QString privKey; // client private key
QString clientCert; // client signed certificate
QString caCert; // server certificate
QString taKey; // tls-auth key
QString host; // host ip
};
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData createCertRequest();
private:
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode);
ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId);
};
#endif // OPENVPN_CONFIGURATOR_H

View File

@@ -0,0 +1,40 @@
#include "shadowsocks_configurator.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
QString ssKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
ssKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return "";
}
QJsonObject config;
config.insert("server", credentials.hostName);
config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT");
config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT");
config.insert("password", ssKey);
config.insert("timeout", 60);
config.insert("method", "$SHADOWSOCKS_CIPHER");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
// qDebug().noquote() << textCfg;
return textCfg;
}

View File

@@ -0,0 +1,19 @@
#ifndef SHADOWSOCKS_CONFIGURATOR_H
#define SHADOWSOCKS_CONFIGURATOR_H
#include <QObject>
#include "configurator_base.h"
#include "core/defs.h"
class ShadowSocksConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // SHADOWSOCKS_CONFIGURATOR_H

View File

@@ -0,0 +1,112 @@
#include "ssh_configurator.h"
#include <QDebug>
#include <QObject>
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
#include <QTemporaryFile>
#include <QThread>
#include <qtimer.h>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "core/server_defs.h"
#include "utilities.h"
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
QString SshConfigurator::convertOpenSShKey(const QString &key)
{
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QTemporaryFile tmp;
#ifdef QT_DEBUG
tmp.setAutoRemove(false);
#endif
tmp.open();
tmp.write(key.toUtf8());
tmp.close();
// ssh-keygen -p -P "" -N "" -m pem -f id_ssh
#ifdef Q_OS_WIN
p.setProcessEnvironment(prepareEnv());
p.setProgram("cmd.exe");
p.setNativeArguments(QString("/C \"ssh-keygen.exe -p -P \"\" -N \"\" -m pem -f \"%1\"\"").arg(tmp.fileName()));
#else
p.setProgram("ssh-keygen");
p.setArguments(QStringList() << "-p"
<< "-P"
<< ""
<< "-N"
<< ""
<< "-m"
<< "pem"
<< "-f" << tmp.fileName());
#endif
p.start();
p.waitForFinished();
qDebug().noquote() << "OpenVpnConfigurator::convertOpenSShKey" << p.exitCode() << p.exitStatus() << p.readAll();
tmp.open();
return tmp.readAll();
#else
return key;
#endif
}
// DEAD CODE.
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
{
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
QProcess *p = new QProcess();
p->setProcessChannelMode(QProcess::SeparateChannels);
#ifdef Q_OS_WIN
p->setProcessEnvironment(prepareEnv());
p->setProgram(qApp->applicationDirPath() + "\\cygwin\\putty.exe");
if (credentials.secretData.contains("PRIVATE KEY")) {
// todo: connect by key
// p->setNativeArguments(QString("%1@%2")
// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
} else {
p->setNativeArguments(QString("%1@%2 -pw %3").arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
}
#else
p->setProgram("/bin/bash");
#endif
p->startDetached();
#endif
}
QProcessEnvironment SshConfigurator::prepareEnv()
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString pathEnvVar = env.value("PATH");
#ifdef Q_OS_WIN
pathEnvVar.clear();
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
#endif
env.insert("PATH", pathEnvVar);
// qDebug().noquote() << "ENV PATH" << pathEnvVar;
return env;
}

View File

@@ -0,0 +1,22 @@
#ifndef SSH_CONFIGURATOR_H
#define SSH_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/defs.h"
class SshConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QProcessEnvironment prepareEnv();
QString convertOpenSShKey(const QString &key);
void openSshTerminal(const ServerCredentials &credentials);
};
#endif // SSH_CONFIGURATOR_H

View File

@@ -1,4 +1,4 @@
#include "wireguardConfigurator.h"
#include "wireguard_configurator.h"
#include <QDebug>
#include <QJsonDocument>
@@ -13,26 +13,17 @@
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/utilities.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/wireGuardProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include <QJsonArray>
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "settings.h"
#include "utilities.h"
using namespace amnezia;
WireguardConfigurator::WireguardConfigurator(SshSession* sshSession, bool isAwg,
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
const QSharedPointer<ServerController> &serverController, bool isAwg,
QObject *parent)
: ConfiguratorBase(sshSession, parent), m_isAwg(isAwg)
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
{
m_serverConfigPath =
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
@@ -42,8 +33,8 @@ WireguardConfigurator::WireguardConfigurator(SshSession* sshSession, bool isAwg,
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
m_protocolName = m_isAwg ? configKey::awg : configKey::wireguard;
m_defaultPort = m_isAwg ? protocols::awg::defaultPort : protocols::wireguard::defaultPort;
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
}
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
@@ -100,21 +91,12 @@ QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container,
const WireGuardServerConfig* serverConfig,
const AwgServerConfig* awgServerConfig,
const DnsSettings &dnsSettings,
const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
QString portStr = m_defaultPort;
if (serverConfig && !serverConfig->port.isEmpty()) {
portStr = serverConfig->port;
} else if (awgServerConfig && !awgServerConfig->port.isEmpty()) {
portStr = awgServerConfig->port;
}
connData.port = portStr;
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
errorCode = ErrorCode::InternalError;
@@ -132,7 +114,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return ErrorCode::NoError;
};
errorCode = m_sshSession->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return connData;
}
@@ -141,14 +123,11 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
QHostAddress nextIp = [&] {
QHostAddress result;
QHostAddress lastIp;
QString subnetAddress = protocols::wireguard::defaultSubnetAddress;
if (serverConfig && !serverConfig->subnetAddress.isEmpty()) {
subnetAddress = serverConfig->subnetAddress;
} else if (awgServerConfig && !awgServerConfig->subnetAddress.isEmpty()) {
subnetAddress = awgServerConfig->subnetAddress;
}
if (ips.empty()) {
lastIp.setAddress(subnetAddress);
lastIp.setAddress(containerConfig.value(m_protocolName)
.toObject()
.value(config_key::subnet_address)
.toString(protocols::wireguard::defaultSubnetAddress));
} else {
lastIp = ips.last();
}
@@ -166,13 +145,13 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
// Get keys
connData.serverPubKey =
m_sshSession->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return connData;
}
connData.pskKey = m_sshSession->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
connData.pskKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
@@ -186,7 +165,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
"AllowedIPs = %3/32\n\n")
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
errorCode = m_sshSession->uploadTextFileToContainer(container, credentials, configPart, configPath,
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, configPart, configPath,
libssh::ScpOverwriteMode::ScpAppendToExisting);
if (errorCode != ErrorCode::NoError) {
@@ -199,43 +178,23 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
QString script = QString(
"sudo docker exec -i $CONTAINER_NAME bash -c '%1 syncconf %2 <(%1-quick strip %3)'").arg(bin, iface, configPath);
errorCode = m_sshSession->runScript(
errorCode = m_serverController->runScript(
credentials,
m_sshSession->replaceVars(script, amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns)));
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData;
}
ProtocolConfig WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
const WireGuardServerConfig* wireguardServerConfig = nullptr;
const WireGuardClientConfig* wireguardClientConfig = nullptr;
const AwgServerConfig* awgServerConfig = nullptr;
const AwgClientConfig* awgClientConfig = nullptr;
if (auto* wireGuardProtocolConfig = containerConfig.getWireGuardProtocolConfig()) {
wireguardServerConfig = &wireGuardProtocolConfig->serverConfig;
if (wireGuardProtocolConfig->clientConfig.has_value()) {
wireguardClientConfig = &wireGuardProtocolConfig->clientConfig.value();
}
} else if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
awgServerConfig = &awgProtocolConfig->serverConfig;
if (awgProtocolConfig->clientConfig.has_value()) {
awgClientConfig = &awgProtocolConfig->clientConfig.value();
}
}
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = m_sshSession->replaceVars(scriptData, vars);
QString config = m_serverController->replaceVars(
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, wireguardServerConfig, awgServerConfig, dnsSettings, errorCode);
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return WireGuardProtocolConfig{};
return "";
}
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
@@ -243,46 +202,40 @@ ProtocolConfig WireguardConfigurator::createConfig(const ServerCredentials &cred
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
config.replace("$WIREGUARD_PSK", connData.pskKey);
QString mtu = protocols::wireguard::defaultMtu;
if (wireguardClientConfig && !wireguardClientConfig->mtu.isEmpty()) {
mtu = wireguardClientConfig->mtu;
} else if (awgClientConfig && !awgClientConfig->mtu.isEmpty()) {
mtu = awgClientConfig->mtu;
}
WireGuardProtocolConfig protocolConfig;
if (wireguardServerConfig) {
protocolConfig.serverConfig = *wireguardServerConfig;
}
WireGuardClientConfig clientConfig;
clientConfig.nativeConfig = config;
clientConfig.hostName = connData.host;
clientConfig.port = connData.port.toInt();
clientConfig.clientIp = connData.clientIP;
clientConfig.clientPrivateKey = connData.clientPrivKey;
clientConfig.clientPublicKey = connData.clientPubKey;
clientConfig.serverPublicKey = connData.serverPubKey;
clientConfig.presharedKey = connData.pskKey;
clientConfig.clientId = connData.clientPubKey;
clientConfig.allowedIps = QStringList { "0.0.0.0/0", "::/0" };
clientConfig.persistentKeepAlive = "25";
clientConfig.mtu = mtu;
clientConfig.isObfuscationEnabled = false;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
QJsonObject jConfig;
jConfig[config_key::config] = config;
jConfig[config_key::hostName] = connData.host;
jConfig[config_key::port] = connData.port.toInt();
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
jConfig[config_key::client_ip] = connData.clientIP;
jConfig[config_key::client_pub_key] = connData.clientPubKey;
jConfig[config_key::psk_key] = connData.pskKey;
jConfig[config_key::server_pub_key] = connData.serverPubKey;
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
jConfig[config_key::persistent_keep_alive] = "25";
QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
jConfig[config_key::allowed_ips] = allowedIps;
jConfig[config_key::clientId] = connData.clientPubKey;
return QJsonDocument(jConfig).toJson();
}
ProtocolConfig WireguardConfigurator::processConfigWithLocalSettings(const ConnectionSettings &settings,
ProtocolConfig protocolConfig)
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
return ConfiguratorBase::processConfigWithLocalSettings(settings, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
ProtocolConfig WireguardConfigurator::processConfigWithExportSettings(const ExportSettings &settings,
ProtocolConfig protocolConfig)
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
return ConfiguratorBase::processConfigWithExportSettings(settings, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}

View File

@@ -0,0 +1,54 @@
#ifndef WIREGUARD_CONFIGURATOR_H
#define WIREGUARD_CONFIGURATOR_H
#include <QHostAddress>
#include <QObject>
#include <QProcessEnvironment>
#include "configurator_base.h"
#include "core/defs.h"
#include "core/scripts_registry.h"
class WireguardConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
bool isAwg, QObject *parent = nullptr);
struct ConnectionData
{
QString clientPrivKey; // client private key
QString clientPubKey; // client public key
QString clientIP; // internal client IP address
QString serverPubKey; // tls-auth key
QString pskKey; // preshared key
QString host; // host ip
QString port;
};
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData genClientKeys();
private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
bool m_isAwg;
QString m_serverConfigPath;
QString m_serverPublicKeyPath;
QString m_serverPskKeyPath;
amnezia::ProtocolScriptType m_configTemplate;
QString m_protocolName;
QString m_defaultPort;
};
#endif // WIREGUARD_CONFIGURATOR_H

View File

@@ -1,43 +1,32 @@
#include "xrayConfigurator.h"
#include "xray_configurator.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QUuid>
#include "logger.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
namespace {
Logger logger("XrayConfigurator");
}
XrayConfigurator::XrayConfigurator(SshSession* sshSession, QObject *parent)
: ConfiguratorBase(sshSession, parent)
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
// Generate new UUID for client
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
// Get current server config
QString currentConfig = m_sshSession->getTextFileFromContainer(
QString currentConfig = m_serverController->getTextFileFromContainer(
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
if (errorCode != ErrorCode::NoError) {
@@ -56,13 +45,13 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
QJsonObject serverConfig = doc.object();
// Validate server config structure
if (!serverConfig.contains(amnezia::protocols::xray::inbounds)) {
if (!serverConfig.contains("inbounds")) {
logger.error() << "Server config missing 'inbounds' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonArray inbounds = serverConfig[amnezia::protocols::xray::inbounds].toArray();
QJsonArray inbounds = serverConfig["inbounds"].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Server config has empty 'inbounds' array";
errorCode = ErrorCode::InternalError;
@@ -70,38 +59,38 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains(amnezia::protocols::xray::settings)) {
if (!inbound.contains("settings")) {
logger.error() << "Inbound missing 'settings' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonObject settings = inbound[amnezia::protocols::xray::settings].toObject();
if (!settings.contains(amnezia::protocols::xray::clients)) {
QJsonObject settings = inbound["settings"].toObject();
if (!settings.contains("clients")) {
logger.error() << "Settings missing 'clients' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonArray clients = settings[amnezia::protocols::xray::clients].toArray();
QJsonArray clients = settings["clients"].toArray();
// Create configuration for new client
QJsonObject clientConfig {
{amnezia::protocols::xray::id, clientId},
{amnezia::protocols::xray::flow, "xtls-rprx-vision"}
{"id", clientId},
{"flow", "xtls-rprx-vision"}
};
clients.append(clientConfig);
// Update config
settings[amnezia::protocols::xray::clients] = clients;
inbound[amnezia::protocols::xray::settings] = settings;
settings["clients"] = clients;
inbound["settings"] = settings;
inbounds[0] = inbound;
serverConfig[amnezia::protocols::xray::inbounds] = inbounds;
serverConfig["inbounds"] = inbounds;
// Save updated config to server
QString updatedConfig = QJsonDocument(serverConfig).toJson();
errorCode = m_sshSession->uploadTextFileToContainer(
errorCode = m_serverController->uploadTextFileToContainer(
container,
credentials,
updatedConfig,
@@ -115,9 +104,9 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
// Restart container
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
errorCode = m_sshSession->runScript(
errorCode = m_serverController->runScript(
credentials,
m_sshSession->replaceVars(restartScript, amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns))
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
);
if (errorCode != ErrorCode::NoError) {
@@ -128,75 +117,57 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
return clientId;
}
ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
const XrayServerConfig* serverConfig = nullptr;
if (auto* xrayConfig = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
serverConfig = &xrayConfig->serverConfig;
}
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, dnsSettings, errorCode);
// Get client ID from prepareServerConfig
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
logger.error() << "Failed to prepare server config";
errorCode = ErrorCode::InternalError;
return XrayProtocolConfig{};
return "";
}
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
QString config = m_sshSession->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container), vars);
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
if (config.isEmpty()) {
logger.error() << "Failed to get config template";
errorCode = ErrorCode::InternalError;
return XrayProtocolConfig{};
return "";
}
QString xrayPublicKey =
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
logger.error() << "Failed to get public key";
errorCode = ErrorCode::InternalError;
return XrayProtocolConfig{};
return "";
}
xrayPublicKey.replace("\n", "");
QString xrayShortId =
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
logger.error() << "Failed to get short ID";
errorCode = ErrorCode::InternalError;
return XrayProtocolConfig{};
return "";
}
xrayShortId.replace("\n", "");
// Validate all required variables are present
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
logger.error() << "Config template missing required variables:"
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
errorCode = ErrorCode::InternalError;
return XrayProtocolConfig{};
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayClientId);
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
config.replace("$XRAY_SHORT_ID", xrayShortId);
XrayProtocolConfig protocolConfig;
if (serverConfig) {
protocolConfig.serverConfig = *serverConfig;
}
XrayClientConfig clientConfig;
clientConfig.nativeConfig = config;
clientConfig.localPort = "";
clientConfig.id = xrayClientId;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
return config;
}

View File

@@ -0,0 +1,23 @@
#ifndef XRAY_CONFIGURATOR_H
#define XRAY_CONFIGURATOR_H
#include <QObject>
#include "configurator_base.h"
#include "core/defs.h"
class XrayConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
private:
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -1,12 +1,17 @@
#include "containerUtils.h"
#include "containers_defs.h"
#include <QMetaEnum>
#include <QObject>
#include <QJsonDocument>
#include "QJsonObject"
#include "QJsonDocument"
using namespace amnezia;
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c)
{
QDebugStateSaver saver(debug);
debug.nospace() << ContainerProps::containerToString(c);
DockerContainer ContainerUtils::containerFromString(const QString &container)
return debug;
}
amnezia::DockerContainer ContainerProps::containerFromString(const QString &container)
{
QMetaEnum metaEnum = QMetaEnum::fromType<DockerContainer>();
for (int i = 0; i < metaEnum.keyCount(); ++i) {
@@ -17,10 +22,12 @@ DockerContainer ContainerUtils::containerFromString(const QString &container)
return DockerContainer::None;
}
QString ContainerUtils::containerToString(DockerContainer c)
QString ContainerProps::containerToString(amnezia::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)
@@ -31,7 +38,7 @@ QString ContainerUtils::containerToString(DockerContainer c)
return "amnezia-" + containerKey.toLower();
}
QString ContainerUtils::containerTypeToString(DockerContainer c)
QString ContainerProps::containerTypeToString(amnezia::DockerContainer c)
{
if (c == DockerContainer::None)
return "none";
@@ -47,7 +54,36 @@ QString ContainerUtils::containerTypeToString(DockerContainer c)
return containerKey.toLower();
}
QList<DockerContainer> ContainerUtils::allContainers()
QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerContainer container)
{
switch (container) {
case DockerContainer::None: return {};
case DockerContainer::OpenVpn: return { Proto::OpenVpn };
case DockerContainer::ShadowSocks: return { Proto::OpenVpn, Proto::ShadowSocks };
case DockerContainer::Cloak: return { Proto::OpenVpn, Proto::ShadowSocks, Proto::Cloak };
case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ };
case DockerContainer::Xray: return { Proto::Xray };
case DockerContainer::SSXray: return { Proto::SSXray };
case DockerContainer::Dns: return { Proto::Dns };
case DockerContainer::Sftp: return { Proto::Sftp };
case DockerContainer::Socks5Proxy: return { Proto::Socks5Proxy };
case DockerContainer::Awg: return { Proto::Awg };
case DockerContainer::Awg2: return { Proto::Awg };
default: return { defaultProtocol(container) };
}
}
QList<DockerContainer> ContainerProps::allContainers()
{
QMetaEnum metaEnum = QMetaEnum::fromType<DockerContainer>();
QList<DockerContainer> all;
@@ -58,10 +94,12 @@ QList<DockerContainer> ContainerUtils::allContainers()
return all;
}
QMap<DockerContainer, QString> ContainerUtils::containerHumanNames()
QMap<DockerContainer, QString> ContainerProps::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" },
@@ -75,11 +113,16 @@ QMap<DockerContainer, QString> ContainerUtils::containerHumanNames()
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
}
QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
{
return { { DockerContainer::OpenVpn,
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("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
{ DockerContainer::Cloak,
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
"active-probing detection. It is very resistant to detection, but offers low speed.") },
{ DockerContainer::WireGuard,
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
"consumption.") },
@@ -105,7 +148,7 @@ QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
QObject::tr("") } };
}
QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{
return {
{ DockerContainer::OpenVpn,
@@ -119,6 +162,28 @@ QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
"* Normal battery consumption on mobile devices\n"
"* Flexible customization for various devices and OS\n"
"* Operates over both TCP and UDP protocols") },
{ DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
"\nFeatures:\n"
"* Available in AmneziaVPN only on desktop platforms\n"
"* Customizable encryption protocol\n"
"* Detectable by some DPI systems\n"
"* Operates over TCP protocol\n") },
{ DockerContainer::Cloak,
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
"\nThe Cloak plugin further protects the connection from DPI detection. "
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
"\nFeatures:\n"
"* Available on all AmneziaVPN platforms\n"
"* High power consumption on mobile devices\n"
"* Flexible configuration options\n"
"* Undetectable by DPI systems\n"
"* Operates over TCP protocol on port 443") },
{ DockerContainer::WireGuard,
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
@@ -176,16 +241,18 @@ QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
};
}
ServiceType ContainerUtils::containerService(DockerContainer c)
amnezia::ServiceType ContainerProps::containerService(DockerContainer c)
{
return ProtocolUtils::protocolService(defaultProtocol(c));
return ProtocolProps::protocolService(defaultProtocol(c));
}
Proto ContainerUtils::defaultProtocol(DockerContainer c)
Proto ContainerProps::defaultProtocol(DockerContainer c)
{
switch (c) {
case DockerContainer::None: return Proto::Unknown;
case DockerContainer::None: return Proto::Any;
case DockerContainer::OpenVpn: return Proto::OpenVpn;
case DockerContainer::Cloak: return Proto::Cloak;
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
case DockerContainer::WireGuard: return Proto::WireGuard;
case DockerContainer::Awg2: return Proto::Awg;
case DockerContainer::Awg: return Proto::Awg;
@@ -197,20 +264,20 @@ Proto ContainerUtils::defaultProtocol(DockerContainer c)
case DockerContainer::Dns: return Proto::Dns;
case DockerContainer::Sftp: return Proto::Sftp;
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
default: return Proto::Unknown;
default: return Proto::Any;
}
}
QString ContainerUtils::containerTypeToProtocolString(DockerContainer c)
QString ContainerProps::containerTypeToProtocolString(DockerContainer c)
{
if (c == DockerContainer::None)
return "none";
Proto p = defaultProtocol(c);
return ProtocolUtils::protoToString(p);
return ProtocolProps::protoToString(p);
}
bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
{
#ifdef Q_OS_WINDOWS
return true;
@@ -223,20 +290,24 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg2: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::Cloak: return true;
case DockerContainer::SSXray: return true;
// case DockerContainer::ShadowSocks: return true;
default:
return false;
}
#elif defined(MACOS_NE)
// macOS build using Network Extension allow OpenVPN for parity with iOS.
// macOS build using Network Extension hide OpenVPN-based containers
switch (c) {
case DockerContainer::OpenVpn: return true;
case DockerContainer::WireGuard: return true;
case DockerContainer::Awg2: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true;
case DockerContainer::OpenVpn:
case DockerContainer::Cloak:
case DockerContainer::ShadowSocks:
return false;
default:
return false;
@@ -252,8 +323,10 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::OpenVpn: return true;
case DockerContainer::ShadowSocks: return false;
case DockerContainer::Awg2: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Cloak: return true;
case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true;
default: return false;
@@ -270,7 +343,7 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
#endif
}
QStringList ContainerUtils::fixedPortsForContainer(DockerContainer c)
QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
{
switch (c) {
case DockerContainer::Ipsec: return QStringList { "500", "4500" };
@@ -278,7 +351,7 @@ QStringList ContainerUtils::fixedPortsForContainer(DockerContainer c)
}
}
bool ContainerUtils::isEasySetupContainer(DockerContainer container)
bool ContainerProps::isEasySetupContainer(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg2: return true;
@@ -286,24 +359,24 @@ bool ContainerUtils::isEasySetupContainer(DockerContainer container)
}
}
QString ContainerUtils::easySetupHeader(DockerContainer container)
QString ContainerProps::easySetupHeader(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg2: return QObject::tr("Automatic");
case DockerContainer::Awg2: return tr("Automatic");
default: return "";
}
}
QString ContainerUtils::easySetupDescription(DockerContainer container)
QString ContainerProps::easySetupDescription(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg2: return QObject::tr("AmneziaWG protocol will be installed. "
case DockerContainer::Awg2: return tr("AmneziaWG protocol will be installed. "
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.");
default: return "";
}
}
int ContainerUtils::easySetupOrder(DockerContainer container)
int ContainerProps::easySetupOrder(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg2: return 1;
@@ -311,7 +384,7 @@ int ContainerUtils::easySetupOrder(DockerContainer container)
}
}
bool ContainerUtils::isShareable(DockerContainer container)
bool ContainerProps::isShareable(DockerContainer container)
{
switch (container) {
case DockerContainer::TorWebSite: return false;
@@ -322,25 +395,28 @@ bool ContainerUtils::isShareable(DockerContainer container)
}
}
bool ContainerUtils::isAwgContainer(DockerContainer container)
bool ContainerProps::isAwgContainer(DockerContainer container)
{
return container == DockerContainer::Awg || container == DockerContainer::Awg2;
}
QJsonObject ContainerUtils::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
{
QString protocolConfigString = containerConfig.value(ProtocolUtils::protoToString(protocol))
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
.toObject()
.value(configKey::lastConfig)
.value(config_key::last_config)
.toString();
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
}
int ContainerUtils::installPageOrder(DockerContainer container)
int ContainerProps::installPageOrder(DockerContainer container)
{
switch (container) {
case DockerContainer::OpenVpn: return 4;
case DockerContainer::Cloak: return 5;
case DockerContainer::ShadowSocks: return 6;
case DockerContainer::WireGuard: return 2;
case DockerContainer::Awg2: return 1;
case DockerContainer::Xray: return 3;
@@ -349,5 +425,3 @@ int ContainerUtils::installPageOrder(DockerContainer container)
default: return 0;
}
}

View File

@@ -0,0 +1,94 @@
#ifndef CONTAINERS_DEFS_H
#define CONTAINERS_DEFS_H
#include <QObject>
#include <QQmlEngine>
#include "../protocols/protocols_defs.h"
using namespace amnezia;
namespace amnezia
{
namespace ContainerEnumNS
{
Q_NAMESPACE
enum DockerContainer {
None = 0,
Awg,
Awg2,
WireGuard,
OpenVpn,
Cloak,
ShadowSocks,
Ipsec,
Xray,
SSXray,
// non-vpn
TorWebSite,
Dns,
Sftp,
Socks5Proxy
};
Q_ENUM_NS(DockerContainer)
} // namespace ContainerEnumNS
using namespace ContainerEnumNS;
using namespace ProtocolEnumNS;
class ContainerProps : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE static amnezia::DockerContainer containerFromString(const QString &container);
Q_INVOKABLE static QString containerToString(amnezia::DockerContainer container);
Q_INVOKABLE static QString containerTypeToString(amnezia::DockerContainer c);
Q_INVOKABLE static QString containerTypeToProtocolString(amnezia::DockerContainer c);
Q_INVOKABLE static QList<amnezia::DockerContainer> allContainers();
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerHumanNames();
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerDescriptions();
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerDetailedDescriptions();
// these protocols will be displayed in container settings
Q_INVOKABLE static QVector<amnezia::Proto> protocolsForContainer(amnezia::DockerContainer container);
Q_INVOKABLE static amnezia::ServiceType containerService(amnezia::DockerContainer c);
// binding between Docker container and main protocol of given container
// it may be changed fot future containers :)
Q_INVOKABLE static amnezia::Proto defaultProtocol(amnezia::DockerContainer c);
Q_INVOKABLE static bool isSupportedByCurrentPlatform(amnezia::DockerContainer c);
Q_INVOKABLE static QStringList fixedPortsForContainer(amnezia::DockerContainer c);
static bool isEasySetupContainer(amnezia::DockerContainer container);
static QString easySetupHeader(amnezia::DockerContainer container);
static QString easySetupDescription(amnezia::DockerContainer container);
static int easySetupOrder(amnezia::DockerContainer container);
static bool isShareable(amnezia::DockerContainer container);
static bool isAwgContainer(amnezia::DockerContainer container);
static QJsonObject getProtocolConfigFromContainer(const amnezia::Proto protocol, const QJsonObject &containerConfig);
static int installPageOrder(amnezia::DockerContainer container);
};
static void declareQmlContainerEnum()
{
qmlRegisterUncreatableMetaObject(ContainerEnumNS::staticMetaObject, "ContainerEnum", 1, 0, "ContainerEnum",
"Error: only enums");
}
} // namespace amnezia
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c);
#endif // CONTAINERS_DEFS_H

View File

@@ -1,58 +1,60 @@
#ifndef APIKEYS_H
#define APIKEYS_H
#ifndef APIDEFS_H
#define APIDEFS_H
#include <QLatin1String>
#include "core/utils/api/apiEnums.h"
#include <QString>
namespace apiDefs
{
enum ConfigType {
AmneziaFreeV2 = 0,
AmneziaFreeV3,
AmneziaPremiumV1,
AmneziaPremiumV2,
SelfHosted,
ExternalPremium
};
enum ConfigSource {
Telegram = 1,
AmneziaGateway
};
namespace key
{
constexpr QLatin1String configVersion("config_version");
constexpr QLatin1String apiEndpoint("api_endpoint");
constexpr QLatin1String apiKey("api_key");
constexpr QLatin1String description("description");
constexpr QLatin1String name("name");
constexpr QLatin1String protocol("protocol");
constexpr QLatin1String apiConfig("api_config");
constexpr QLatin1String serviceType("service_type");
constexpr QLatin1String serviceInfo("service_info");
constexpr QLatin1String serviceProtocol("service_protocol");
constexpr QLatin1String vpnKey("vpn_key");
constexpr QLatin1String stackType("stack_type");
constexpr QLatin1String serviceType("service_type");
constexpr QLatin1String cliVersion("cli_version");
constexpr QLatin1String cliName("cli_name");
constexpr QLatin1String supportedProtocols("supported_protocols");
constexpr QLatin1String availableCountries("available_countries");
constexpr QLatin1String vpnKey("vpn_key");
constexpr QLatin1String config("config");
constexpr QLatin1String configs("configs");
constexpr QLatin1String installationUuid("installation_uuid");
constexpr QLatin1String uuid("installation_uuid");
constexpr QLatin1String osVersion("os_version");
constexpr QLatin1String userCountryCode("user_country_code");
constexpr QLatin1String serverCountryCode("server_country_code");
constexpr QLatin1String serverCountryName("server_country_name");
constexpr QLatin1String appVersion("app_version");
constexpr QLatin1String authData("auth_data");
constexpr QLatin1String aesKey("aes_key");
constexpr QLatin1String aesIv("aes_iv");
constexpr QLatin1String aesSalt("aes_salt");
constexpr QLatin1String apiPayload("api_payload");
constexpr QLatin1String keyPayload("key_payload");
constexpr QLatin1String services("services");
constexpr QLatin1String workerLastUpdated("worker_last_updated");
constexpr QLatin1String lastDownloaded("last_downloaded");
constexpr QLatin1String sourceType("source_type");
constexpr QLatin1String serverCountryCode("server_country_code");
constexpr QLatin1String serverCountryName("server_country_name");
constexpr QLatin1String osVersion("os_version");
constexpr QLatin1String appLanguage("app_language");
constexpr QLatin1String availableCountries("available_countries");
constexpr QLatin1String activeDeviceCount("active_device_count");
constexpr QLatin1String maxDeviceCount("max_device_count");
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
constexpr QLatin1String subscriptionExpiredByServer("subscription_expired_by_server");
constexpr QLatin1String subscriptionStatus("subscription_status");
constexpr QLatin1String subscription("subscription");
constexpr QLatin1String endDate("end_date");
constexpr QLatin1String issuedConfigs("issued_configs");
constexpr QLatin1String subscriptionDescription("subscription_description");
constexpr QLatin1String termsOfUseUrl("terms_of_use_url");
constexpr QLatin1String privacyPolicyUrl("privacy_policy_url");
constexpr QLatin1String supportInfo("support_info");
constexpr QLatin1String email("email");
@@ -64,25 +66,20 @@ namespace apiDefs
constexpr QLatin1String id("id");
constexpr QLatin1String orderId("order_id");
constexpr QLatin1String migrationCode("migration_code");
constexpr QLatin1String transactionId("transaction_id");
constexpr QLatin1String isTestPurchase("is_test_purchase");
constexpr QLatin1String isInAppPurchase("is_in_app_purchase");
constexpr QLatin1String config("config");
constexpr QLatin1String userCountryCode("user_country_code");
constexpr QLatin1String serviceInfo("service_info");
constexpr QLatin1String isAdVisible("is_ad_visible");
constexpr QLatin1String isRenewalAvailable("is_renewal_available");
constexpr QLatin1String adHeader("ad_header");
constexpr QLatin1String adDescription("ad_description");
constexpr QLatin1String adEndpoint("ad_endpoint");
constexpr QLatin1String configs("configs");
constexpr QLatin1String publicKeyInfo("public_key");
constexpr QLatin1String publicKey("public_key");
constexpr QLatin1String expiresAt("expires_at");
constexpr QLatin1String isConnectEvent("is_connect_event");
constexpr QLatin1String certificate("certificate");
}
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
}
#endif // APIKEYS_H
#endif // APIDEFS_H

View File

@@ -1,38 +1,13 @@
#include "apiUtils.h"
#include "core/utils/constants/configKeys.h"
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
using namespace amnezia;
namespace
{
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
constexpr QLatin1String unprocessableSubscriptionMessage("Failed to retrieve subscription information. Is it activated?");
constexpr QLatin1String trialAlreadyUsedMessage("trial subscription already used");
QDateTime subscriptionEndUtcFromString(const QString &subscriptionEndDate)
{
if (subscriptionEndDate.isEmpty()) {
return {};
}
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs).toUTC();
if (!endDate.isValid()) {
endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODate).toUTC();
}
return endDate;
}
QString apiErrorMessageFromJson(const QJsonObject &jsonObj)
{
const QJsonValue value = jsonObj.value(QStringLiteral("message"));
return value.isString() ? value.toString().trimmed() : QString();
}
QString escapeUnicode(const QString &input)
{
QString output;
@@ -49,35 +24,14 @@ namespace
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
{
if (subscriptionEndDate.isEmpty()) {
return false;
}
const QDateTime endDate = subscriptionEndUtcFromString(subscriptionEndDate);
if (!endDate.isValid()) {
return false;
}
return endDate <= QDateTime::currentDateTimeUtc();
}
bool apiUtils::isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays)
{
if (subscriptionEndDate.isEmpty()) {
return false;
}
const QDateTime endDate = subscriptionEndUtcFromString(subscriptionEndDate);
if (!endDate.isValid()) {
return false;
}
const QDateTime nowUtc = QDateTime::currentDateTimeUtc();
if (endDate <= nowUtc) {
return false;
}
return endDate <= nowUtc.addDays(withinDays);
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
return endDate < now;
}
bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) {
case apiDefs::ConfigSource::Telegram: return true;
case apiDefs::ConfigSource::AmneziaGateway: return true;
@@ -87,7 +41,7 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) {
case apiDefs::ConfigSource::Telegram: {
@@ -106,7 +60,6 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium");
constexpr QLatin1String serviceExternalTrial("external-trial");
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
@@ -117,8 +70,6 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
return apiDefs::ConfigType::AmneziaFreeV3;
} else if (serviceType == serviceExternalPremium) {
return apiDefs::ConfigType::ExternalPremium;
} else if (serviceType == serviceExternalTrial) {
return apiDefs::ConfigType::ExternalTrial;
}
}
default: {
@@ -129,7 +80,7 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
{
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(configKey::configVersion).toInt());
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
}
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
@@ -139,66 +90,50 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
const int httpStatusCodeConflict = 409;
const int httpStatusCodeNotFound = 404;
const int httpStatusCodeNotImplemented = 501;
const int httpStatusCodePaymentRequired = 402;
const int httpStatusCodeUnprocessableEntity = 422;
if (!sslErrors.empty()) {
qDebug().noquote() << sslErrors;
return amnezia::ErrorCode::ApiConfigSslError;
}
if (replyError == QNetworkReply::NoError) {
} else if (replyError == QNetworkReply::NoError) {
return amnezia::ErrorCode::NoError;
}
if (replyError == QNetworkReply::NetworkError::OperationCanceledError
|| replyError == QNetworkReply::NetworkError::TimeoutError) {
} else if (replyError == QNetworkReply::NetworkError::OperationCanceledError
|| replyError == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << replyError;
return amnezia::ErrorCode::ApiConfigTimeoutError;
}
if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << replyError;
return amnezia::ErrorCode::ApiUpdateRequestError;
}
} else {
qDebug() << QString::fromUtf8(responseBody);
qDebug() << replyError;
qDebug() << replyErrorString;
qDebug() << httpStatusCode;
qDebug() << QString::fromUtf8(responseBody);
qDebug() << replyError;
qDebug() << httpStatusCode;
int httpStatusFromBody = -1;
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
if (jsonDoc.isObject()) {
QJsonObject jsonObj = jsonDoc.object();
httpStatusFromBody = jsonObj.value("http_status").toInt(-1);
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
if (jsonDoc.isObject()) {
QJsonObject jsonObj = jsonDoc.object();
const int httpStatusFromBody = jsonObj.value(QStringLiteral("http_status")).toInt(-1);
if (httpStatusFromBody == httpStatusCodeConflict) {
if (apiErrorMessageFromJson(jsonObj).contains(trialAlreadyUsedMessage, Qt::CaseInsensitive)) {
return amnezia::ErrorCode::ApiTrialAlreadyUsedError;
}
return amnezia::ErrorCode::ApiConfigLimitError;
}
if (httpStatusFromBody == httpStatusCodeNotFound) {
} else if (httpStatusFromBody == httpStatusCodeNotFound) {
return amnezia::ErrorCode::ApiNotFoundError;
}
if (httpStatusFromBody == httpStatusCodeNotImplemented) {
} else if (httpStatusFromBody == httpStatusCodeNotImplemented) {
return amnezia::ErrorCode::ApiUpdateRequestError;
}
if (httpStatusFromBody == httpStatusCodeUnprocessableEntity) {
if (apiErrorMessageFromJson(jsonObj) == unprocessableSubscriptionMessage) {
return amnezia::ErrorCode::ApiSubscriptionExpiredError;
}
return amnezia::ErrorCode::ApiConfigDownloadError;
}
if (httpStatusFromBody == httpStatusCodePaymentRequired) {
return amnezia::ErrorCode::ApiSubscriptionNotActiveError;
}
return amnezia::ErrorCode::ApiConfigDownloadError;
}
qDebug() << "something went wrong";
return amnezia::ErrorCode::ApiConfigDownloadError;
return amnezia::ErrorCode::InternalError;
}
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
{
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
apiDefs::ConfigType::ExternalPremium, apiDefs::ConfigType::ExternalTrial };
apiDefs::ConfigType::ExternalPremium };
return premiumTypes.contains(getConfigType(serverConfigObject));
}
@@ -209,9 +144,9 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
}
QList<QPair<QString, QVariant>> orderedFields;
orderedFields.append(qMakePair(configKey::name, serverConfigObject[configKey::name].toString()));
orderedFields.append(qMakePair(configKey::description, serverConfigObject[configKey::description].toString()));
orderedFields.append(qMakePair(configKey::configVersion, serverConfigObject[configKey::configVersion].toDouble()));
orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString()));
orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString()));
orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble()));
orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString()));
orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString()));
orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString()));
@@ -242,9 +177,7 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{
auto configType = apiUtils::getConfigType(serverConfigObject);
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::ExternalPremium
&& configType != apiDefs::ConfigType::ExternalTrial) {
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
return {};
}
@@ -253,9 +186,9 @@ QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
auto apiConfig = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto authData = serverConfigObject.value(QLatin1String("auth_data")).toObject();
const QString name = serverConfigObject.value(configKey::name).toString();
const QString description = serverConfigObject.value(configKey::description).toString();
const double configVersion = serverConfigObject.value(configKey::configVersion).toDouble();
const QString name = serverConfigObject.value(apiDefs::key::name).toString();
const QString description = serverConfigObject.value(apiDefs::key::description).toString();
const double configVersion = serverConfigObject.value(apiDefs::key::configVersion).toDouble();
const QString serviceType = apiConfig.value(apiDefs::key::serviceType).toString();
const QString serviceProtocol = apiConfig.value(QLatin1String("service_protocol")).toString();
@@ -264,9 +197,9 @@ QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
const QString apiKey = authData.value(apiDefs::key::apiKey).toString();
QString vpnKeyStr = "{";
vpnKeyStr += "\"" + QString(configKey::name) + "\": \"" + name + "\", ";
vpnKeyStr += "\"" + QString(configKey::description) + "\": \"" + description + "\", ";
vpnKeyStr += "\"" + QString(configKey::configVersion) + "\": " + QString::number(static_cast<int>(configVersion)) + ", ";
vpnKeyStr += "\"" + QString(apiDefs::key::name) + "\": \"" + name + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::description) + "\": \"" + description + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::configVersion) + "\": " + QString::number(static_cast<int>(configVersion)) + ", ";
vpnKeyStr += "\"" + QString(apiDefs::key::apiConfig) + "\": {";
vpnKeyStr += "\"" + QString(apiDefs::key::serviceType) + "\": \"" + serviceType + "\", ";

View File

@@ -4,12 +4,8 @@
#include <QNetworkReply>
#include <QObject>
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "apiDefs.h"
#include "core/defs.h"
namespace apiUtils
{
@@ -17,8 +13,6 @@ namespace apiUtils
bool isSubscriptionExpired(const QString &subscriptionEndDate);
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
bool isPremiumServer(const QJsonObject &serverConfigObject);
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);

View File

@@ -1,109 +0,0 @@
#include "awgConfigurator.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include <QJsonDocument>
#include <QJsonObject>
using namespace amnezia;
AwgConfigurator::AwgConfigurator(SshSession* sshSession, QObject *parent)
: WireguardConfigurator(sshSession, true, parent)
{
}
ProtocolConfig AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
{
const AwgServerConfig* serverConfig = nullptr;
const AwgClientConfig* clientConfig = nullptr;
if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
serverConfig = &awgProtocolConfig->serverConfig;
if (awgProtocolConfig->clientConfig.has_value()) {
clientConfig = &awgProtocolConfig->clientConfig.value();
}
}
ProtocolConfig wireguardConfig = WireguardConfigurator::createConfig(credentials, container, containerConfig, dnsSettings, errorCode);
if (errorCode != ErrorCode::NoError) {
return AwgProtocolConfig{};
}
WireGuardProtocolConfig* wgConfig = wireguardConfig.as<WireGuardProtocolConfig>();
if (!wgConfig || !wgConfig->clientConfig.has_value()) {
errorCode = ErrorCode::InternalError;
return AwgProtocolConfig{};
}
QString awgConfig = wgConfig->clientConfig->nativeConfig;
QMap<QString, QString> configMap;
auto configLines = awgConfig.split("\n");
for (auto &line : configLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
AwgProtocolConfig protocolConfig;
if (serverConfig) {
protocolConfig.serverConfig = *serverConfig;
}
AwgClientConfig newClientConfig;
newClientConfig.nativeConfig = awgConfig;
newClientConfig.hostName = wgConfig->clientConfig->hostName;
newClientConfig.port = wgConfig->clientConfig->port;
newClientConfig.clientIp = wgConfig->clientConfig->clientIp;
newClientConfig.clientPrivateKey = wgConfig->clientConfig->clientPrivateKey;
newClientConfig.clientPublicKey = wgConfig->clientConfig->clientPublicKey;
newClientConfig.serverPublicKey = wgConfig->clientConfig->serverPublicKey;
newClientConfig.presharedKey = wgConfig->clientConfig->presharedKey;
newClientConfig.clientId = wgConfig->clientConfig->clientId;
newClientConfig.allowedIps = wgConfig->clientConfig->allowedIps;
newClientConfig.persistentKeepAlive = wgConfig->clientConfig->persistentKeepAlive;
QString mtu = protocols::awg::defaultMtu;
if (clientConfig && !clientConfig->mtu.isEmpty()) {
mtu = clientConfig->mtu;
}
newClientConfig.mtu = mtu;
newClientConfig.junkPacketCount = configMap.value(configKey::junkPacketCount);
newClientConfig.junkPacketMinSize = configMap.value(configKey::junkPacketMinSize);
newClientConfig.junkPacketMaxSize = configMap.value(configKey::junkPacketMaxSize);
newClientConfig.initPacketJunkSize = configMap.value(configKey::initPacketJunkSize);
newClientConfig.responsePacketJunkSize = configMap.value(configKey::responsePacketJunkSize);
newClientConfig.initPacketMagicHeader = configMap.value(configKey::initPacketMagicHeader);
newClientConfig.responsePacketMagicHeader = configMap.value(configKey::responsePacketMagicHeader);
newClientConfig.underloadPacketMagicHeader = configMap.value(configKey::underloadPacketMagicHeader);
newClientConfig.transportPacketMagicHeader = configMap.value(configKey::transportPacketMagicHeader);
newClientConfig.specialJunk1 = configMap.value(configKey::specialJunk1);
newClientConfig.specialJunk2 = configMap.value(configKey::specialJunk2);
newClientConfig.specialJunk3 = configMap.value(configKey::specialJunk3);
newClientConfig.specialJunk4 = configMap.value(configKey::specialJunk4);
newClientConfig.specialJunk5 = configMap.value(configKey::specialJunk5);
if (container == DockerContainer::Awg2) {
newClientConfig.cookieReplyPacketJunkSize = configMap.value(configKey::cookieReplyPacketJunkSize);
newClientConfig.transportPacketJunkSize = configMap.value(configKey::transportPacketJunkSize);
}
newClientConfig.isObfuscationEnabled = false;
protocolConfig.setClientConfig(newClientConfig);
return protocolConfig;
}

View File

@@ -1,20 +0,0 @@
#ifndef AWGCONFIGURATOR_H
#define AWGCONFIGURATOR_H
#include <QObject>
#include "wireguardConfigurator.h"
class AwgConfigurator : public WireguardConfigurator
{
Q_OBJECT
public:
AwgConfigurator(SshSession* sshSession, QObject *parent = nullptr);
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) override;
};
#endif // AWGCONFIGURATOR_H

View File

@@ -1,50 +0,0 @@
#include "configuratorBase.h"
#include "core/configurators/awgConfigurator.h"
#include "core/configurators/ikev2Configurator.h"
#include "core/configurators/openVpnConfigurator.h"
#include "core/configurators/wireguardConfigurator.h"
#include "core/configurators/xrayConfigurator.h"
using namespace amnezia;
ConfiguratorBase::ConfiguratorBase(SshSession* sshSession, QObject *parent)
: QObject { parent }, m_sshSession(sshSession)
{
}
QScopedPointer<ConfiguratorBase> ConfiguratorBase::create(Proto protocol,
SshSession* sshSession)
{
switch (protocol) {
case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(sshSession));
case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(sshSession, false));
case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(sshSession));
case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(sshSession));
case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(sshSession));
case Proto::SSXray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(sshSession));
default: return QScopedPointer<ConfiguratorBase>();
}
}
ProtocolConfig ConfiguratorBase::processConfigWithLocalSettings(const ConnectionSettings &settings,
ProtocolConfig protocolConfig)
{
applyDnsToNativeConfig(settings.dns, protocolConfig);
return protocolConfig;
}
ProtocolConfig ConfiguratorBase::processConfigWithExportSettings(const ExportSettings &settings,
ProtocolConfig protocolConfig)
{
applyDnsToNativeConfig(settings.dns, protocolConfig);
return protocolConfig;
}
void ConfiguratorBase::applyDnsToNativeConfig(const DnsSettings &dns, ProtocolConfig &protocolConfig)
{
QString config = protocolConfig.nativeConfig();
config.replace("$PRIMARY_DNS", dns.primaryDns);
config.replace("$SECONDARY_DNS", dns.secondaryDns);
protocolConfig.setNativeConfig(config);
}

View File

@@ -1,43 +0,0 @@
#ifndef CONFIGURATORBASE_H
#define CONFIGURATORBASE_H
#include <QObject>
#include <QScopedPointer>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h"
class SshSession;
class ConfiguratorBase : public QObject
{
Q_OBJECT
public:
explicit ConfiguratorBase(SshSession* sshSession, QObject *parent = nullptr);
static QScopedPointer<ConfiguratorBase> create(amnezia::Proto protocol,
SshSession* sshSession);
virtual amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) = 0;
virtual amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
amnezia::ProtocolConfig protocolConfig);
virtual amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
amnezia::ProtocolConfig protocolConfig);
protected:
void applyDnsToNativeConfig(const amnezia::DnsSettings &dns, amnezia::ProtocolConfig &protocolConfig);
SshSession* m_sshSession;
};
#endif // CONFIGURATORBASE_H

View File

@@ -1,39 +0,0 @@
#ifndef IKEV2_CONFIGURATOR_H
#define IKEV2_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#include "configuratorBase.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
class Ikev2Configurator : public ConfiguratorBase
{
Q_OBJECT
public:
Ikev2Configurator(SshSession* sshSession, QObject *parent = nullptr);
struct ConnectionData {
QByteArray clientCert; // p12 client cert
QByteArray caCert; // p12 server cert
QString clientId;
QString password; // certificate password
QString host; // host ip
};
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) override;
QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData);
ConnectionData prepareIkev2Config(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container, amnezia::ErrorCode &errorCode);
};
#endif // IKEV2_CONFIGURATOR_H

View File

@@ -1,49 +0,0 @@
#ifndef OPENVPN_CONFIGURATOR_H
#define OPENVPN_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#include "configuratorBase.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
class OpenVpnConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
OpenVpnConfigurator(SshSession* sshSession, QObject *parent = nullptr);
struct ConnectionData
{
QString clientId;
QString request; // certificate request
QString privKey; // client private key
QString clientCert; // client signed certificate
QString caCert; // server certificate
QString taKey; // tls-auth key
QString host; // host ip
};
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) override;
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
amnezia::ProtocolConfig protocolConfig) override;
amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
amnezia::ProtocolConfig protocolConfig) override;
static ConnectionData createCertRequest();
private:
ConnectionData prepareOpenVpnConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode);
amnezia::ErrorCode signCert(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
const amnezia::DnsSettings &dnsSettings, QString clientId);
};
#endif // OPENVPN_CONFIGURATOR_H

View File

@@ -1,61 +0,0 @@
#ifndef WIREGUARD_CONFIGURATOR_H
#define WIREGUARD_CONFIGURATOR_H
#include <QHostAddress>
#include <QObject>
#include <QProcessEnvironment>
#include "configuratorBase.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
class WireguardConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
WireguardConfigurator(SshSession* sshSession,
bool isAwg, QObject *parent = nullptr);
struct ConnectionData
{
QString clientPrivKey; // client private key
QString clientPubKey; // client public key
QString clientIP; // internal client IP address
QString serverPubKey; // tls-auth key
QString pskKey; // preshared key
QString host; // host ip
QString port;
};
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) override;
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
amnezia::ProtocolConfig protocolConfig) override;
amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
amnezia::ProtocolConfig protocolConfig) override;
static ConnectionData genClientKeys();
private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::WireGuardServerConfig* serverConfig,
const amnezia::AwgServerConfig* awgServerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode);
bool m_isAwg;
QString m_serverConfigPath;
QString m_serverPublicKeyPath;
QString m_serverPskKeyPath;
amnezia::ProtocolScriptType m_configTemplate;
QString m_protocolName;
QString m_defaultPort;
};
#endif // WIREGUARD_CONFIGURATOR_H

View File

@@ -1,27 +0,0 @@
#ifndef XRAY_CONFIGURATOR_H
#define XRAY_CONFIGURATOR_H
#include <QObject>
#include "configuratorBase.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
class XrayConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
XrayConfigurator(SshSession* sshSession, QObject *parent = nullptr);
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode) override;
private:
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode);
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -1,54 +0,0 @@
#include "allowedDnsController.h"
AllowedDnsController::AllowedDnsController(SecureAppSettingsRepository* appSettingsRepository)
: m_appSettingsRepository(appSettingsRepository)
{
fillDnsServers();
}
bool AllowedDnsController::addDns(const QString &ip)
{
if (m_dnsServers.contains(ip)) {
return false;
}
m_dnsServers.append(ip);
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
return true;
}
void AllowedDnsController::addDnsList(const QStringList &dnsServers, bool replaceExisting)
{
if (replaceExisting) {
m_dnsServers.clear();
}
for (const QString &ip : dnsServers) {
if (!m_dnsServers.contains(ip)) {
m_dnsServers.append(ip);
}
}
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
}
void AllowedDnsController::removeDns(int index)
{
if (index < 0 || index >= m_dnsServers.size()) {
return;
}
m_dnsServers.removeAt(index);
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
}
QStringList AllowedDnsController::getCurrentDnsServers() const
{
return m_dnsServers;
}
void AllowedDnsController::fillDnsServers()
{
m_dnsServers = m_appSettingsRepository->getAllowedDnsServers();
}

View File

@@ -1,26 +0,0 @@
#ifndef ALLOWEDDNSCONTROLLER_H
#define ALLOWEDDNSCONTROLLER_H
#include <QStringList>
#include "core/repositories/secureAppSettingsRepository.h"
class AllowedDnsController
{
public:
explicit AllowedDnsController(SecureAppSettingsRepository* appSettingsRepository);
bool addDns(const QString &ip);
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
void removeDns(int index);
QStringList getCurrentDnsServers() const;
private:
void fillDnsServers();
SecureAppSettingsRepository* m_appSettingsRepository;
QStringList m_dnsServers;
};
#endif // ALLOWEDDNSCONTROLLER_H

View File

@@ -1,72 +0,0 @@
#include "newsController.h"
#include "core/controllers/gatewayController.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/constants/configKeys.h"
#include <QtConcurrent/QtConcurrent>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSharedPointer>
using namespace amnezia;
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository,
ServersController* serversController)
: m_appSettingsRepository(appSettingsRepository), m_serversController(serversController)
{
}
QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
{
if (!m_serversController) {
qWarning() << "ServersController is null, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
}
const auto stacks = m_serversController->gatewayStacks();
if (stacks.isEmpty()) {
qDebug() << "No Gateway stacks, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
}
auto gatewayController = QSharedPointer<GatewayController>::create(
m_appSettingsRepository->getGatewayEndpoint(),
m_appSettingsRepository->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled());
QJsonObject payload;
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
const QJsonObject stacksJson = stacks.toJson();
if (stacksJson.contains(apiDefs::key::userCountryCode)) {
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
}
if (stacksJson.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType));
}
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
return future.then([gatewayController](QPair<ErrorCode, QByteArray> result) -> QPair<ErrorCode, QJsonArray> {
auto [errorCode, responseBody] = result;
if (errorCode != ErrorCode::NoError) {
return qMakePair(errorCode, QJsonArray());
}
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
QJsonArray newsArray;
if (doc.isArray()) {
newsArray = doc.array();
} else if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.value("news").isArray()) {
newsArray = obj.value("news").toArray();
}
}
return qMakePair(ErrorCode::NoError, newsArray);
});
}

View File

@@ -1,28 +0,0 @@
#ifndef NEWSCONTROLLER_H
#define NEWSCONTROLLER_H
#include <QFuture>
#include <QJsonArray>
#include <QPair>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/controllers/serversController.h"
class NewsController
{
public:
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
ServersController* serversController);
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
private:
SecureAppSettingsRepository* m_appSettingsRepository;
ServersController* m_serversController;
};
#endif // NEWSCONTROLLER_H

View File

@@ -1,248 +0,0 @@
#include "servicesCatalogController.h"
#include <QJsonDocument>
#include <QSysInfo>
#include <QJsonArray>
#include <QEventLoop>
#include <QDebug>
#include <QCoreApplication>
#include <QHash>
#include <QSet>
#include <limits>
#include "core/controllers/gatewayController.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "version.h"
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include "platforms/ios/ios_controller.h"
#endif
namespace
{
namespace configKey
{
constexpr char serviceDescription[] = "service_description";
constexpr char subscriptionPlans[] = "subscription_plans";
constexpr char storeProductId[] = "store_product_id";
constexpr char priceLabel[] = "price_label";
constexpr char subtitle[] = "subtitle";
constexpr char isTrial[] = "is_trial";
constexpr char minPriceLabel[] = "min_price_label";
}
namespace serviceType
{
constexpr char amneziaPremium[] = "amnezia-premium";
}
#if defined(Q_OS_IOS) || defined(MACOS_NE)
struct StoreKitPlanQuote {
QString displayPrice;
double priceAmount = 0.0;
double subscriptionBillingMonths = 0.0;
QString displayPricePerMonth;
};
constexpr double oneMonthThreshold = 1.0 + 1e-6;
constexpr double monthsFallbackThreshold = 1e-6;
constexpr double monthlyPriceEpsilon = 1e-9;
QStringList collectPremiumStoreProductIds(const QJsonArray &services)
{
QStringList productIds;
QSet<QString> seenProductIds;
for (const QJsonValue &serviceValue : services) {
const QJsonObject serviceObject = serviceValue.toObject();
if (serviceObject.value(apiDefs::key::serviceType).toString() != serviceType::amneziaPremium) {
continue;
}
const QJsonArray subscriptionPlans =
serviceObject.value(configKey::serviceDescription).toObject().value(configKey::subscriptionPlans).toArray();
for (const QJsonValue &planValue : subscriptionPlans) {
if (!planValue.isObject()) {
continue;
}
const QString storeProductId = planValue.toObject().value(configKey::storeProductId).toString();
if (storeProductId.isEmpty() || seenProductIds.contains(storeProductId)) {
continue;
}
seenProductIds.insert(storeProductId);
productIds.append(storeProductId);
}
}
return productIds;
}
QHash<QString, StoreKitPlanQuote> buildStoreKitQuoteMap(const QList<QVariantMap> &fetchedProducts)
{
QHash<QString, StoreKitPlanQuote> quotesByProductId;
quotesByProductId.reserve(fetchedProducts.size());
for (const QVariantMap &productInfo : fetchedProducts) {
const QString productId = productInfo.value(QStringLiteral("productId")).toString();
if (productId.isEmpty()) {
continue;
}
QString displayPrice = productInfo.value(QStringLiteral("displayPrice")).toString();
if (displayPrice.isEmpty()) {
const QString price = productInfo.value(QStringLiteral("price")).toString();
const QString currencyCode = productInfo.value(QStringLiteral("currencyCode")).toString();
displayPrice = currencyCode.isEmpty() ? price : (price + QLatin1Char(' ') + currencyCode);
}
StoreKitPlanQuote quote;
quote.displayPrice = displayPrice;
quote.priceAmount = productInfo.value(QStringLiteral("priceAmount")).toDouble();
quote.subscriptionBillingMonths = productInfo.value(QStringLiteral("subscriptionBillingMonths")).toDouble();
quote.displayPricePerMonth = productInfo.value(QStringLiteral("displayPricePerMonth")).toString();
quotesByProductId.insert(productId, quote);
}
return quotesByProductId;
}
void mergeStoreKitPricesIntoPremiumPlans(QJsonObject &data)
{
QJsonArray services = data.value(apiDefs::key::services).toArray();
if (services.isEmpty()) {
return;
}
const QStringList productIds = collectPremiumStoreProductIds(services);
if (productIds.isEmpty()) {
qInfo().noquote() << "[IAP] No store_product_id in premium plans; skip StoreKit merge into services payload";
return;
}
QList<QVariantMap> fetchedProducts;
QEventLoop loop;
IosController::Instance()->fetchProducts(productIds,
[&](const QList<QVariantMap> &products, const QStringList &invalidIds,
const QString &errorString) {
if (!errorString.isEmpty()) {
qWarning().noquote() << "[IAP] StoreKit merge fetch:" << errorString;
}
if (!invalidIds.isEmpty()) {
qWarning().noquote() << "[IAP] Unknown App Store product ids:" << invalidIds;
}
fetchedProducts = products;
loop.quit();
});
loop.exec();
const QHash<QString, StoreKitPlanQuote> quotesByProductId = buildStoreKitQuoteMap(fetchedProducts);
for (int serviceIndex = 0; serviceIndex < services.size(); ++serviceIndex) {
QJsonObject serviceObject = services.at(serviceIndex).toObject();
if (serviceObject.value(apiDefs::key::serviceType).toString() != serviceType::amneziaPremium) {
continue;
}
QJsonObject descriptionObject = serviceObject.value(configKey::serviceDescription).toObject();
const QJsonArray sourcePlans = descriptionObject.value(configKey::subscriptionPlans).toArray();
QJsonArray mergedPlans;
double minMonthlyAmount = std::numeric_limits<double>::infinity();
QString minMonthlyDisplay;
for (const QJsonValue &planValue : sourcePlans) {
if (!planValue.isObject()) {
continue;
}
QJsonObject planObject = planValue.toObject();
const QString storeProductId = planObject.value(configKey::storeProductId).toString();
if (storeProductId.isEmpty()) {
continue;
}
const auto quoteIterator = quotesByProductId.constFind(storeProductId);
if (quoteIterator == quotesByProductId.cend()) {
continue;
}
const bool isTrialPlan = planObject.value(configKey::isTrial).toBool();
const StoreKitPlanQuote &quote = *quoteIterator;
planObject.insert(configKey::priceLabel, quote.displayPrice);
const double months = quote.subscriptionBillingMonths;
if (!isTrialPlan && months > oneMonthThreshold && !quote.displayPricePerMonth.isEmpty()) {
planObject.insert(
configKey::subtitle,
QCoreApplication::translate("ServicesCatalogController", "%1/mo",
"IAP: price per month in plan subtitle")
.arg(quote.displayPricePerMonth));
}
if (!isTrialPlan && quote.priceAmount > 0.0) {
const double monthsForMin = months > monthsFallbackThreshold ? months : 1.0;
const double monthly = quote.priceAmount / monthsForMin;
if (monthly < minMonthlyAmount - monthlyPriceEpsilon) {
minMonthlyAmount = monthly;
minMonthlyDisplay = !quote.displayPricePerMonth.isEmpty() ? quote.displayPricePerMonth : quote.displayPrice;
}
}
mergedPlans.append(planObject);
}
descriptionObject.insert(configKey::subscriptionPlans, mergedPlans);
if (minMonthlyAmount < std::numeric_limits<double>::infinity() && !minMonthlyDisplay.isEmpty()) {
descriptionObject.insert(configKey::minPriceLabel,
QCoreApplication::translate("ServicesCatalogController", "from %1 per month",
"IAP: card footer minimum monthly price from StoreKit")
.arg(minMonthlyDisplay));
}
serviceObject.insert(configKey::serviceDescription, descriptionObject);
services.replace(serviceIndex, serviceObject);
}
data.insert(apiDefs::key::services, services);
}
#endif
}
ServicesCatalogController::ServicesCatalogController(SecureAppSettingsRepository* appSettingsRepository)
: m_appSettingsRepository(appSettingsRepository)
{
}
ErrorCode ServicesCatalogController::fillAvailableServices(QJsonObject &servicesData)
{
QJsonObject apiPayload;
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION);
apiPayload[apiDefs::key::cliName] = QString(APPLICATION_NAME);
apiPayload[apiDefs::key::appLanguage] = m_appSettingsRepository->getAppLanguage().name().split("_").first();
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
if (errorCode == ErrorCode::NoError) {
if (!responseBody.contains(apiDefs::key::services.data())) {
errorCode = ErrorCode::ApiServicesMissingError;
}
}
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
servicesData = QJsonDocument::fromJson(responseBody).object();
#if defined(Q_OS_IOS) || defined(MACOS_NE)
mergeStoreKitPricesIntoPremiumPlans(servicesData);
#endif
return ErrorCode::NoError;
}
ErrorCode ServicesCatalogController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody)
{
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled());
return gatewayController.post(endpoint, apiPayload, responseBody);
}

View File

@@ -1,26 +0,0 @@
#ifndef SERVICESCATALOGCONTROLLER_H
#define SERVICESCATALOGCONTROLLER_H
#include <QJsonObject>
#include <QByteArray>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h"
class ServicesCatalogController
{
public:
explicit ServicesCatalogController(SecureAppSettingsRepository* appSettingsRepository);
ErrorCode fillAvailableServices(QJsonObject &servicesData);
private:
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
SecureAppSettingsRepository* m_appSettingsRepository;
};
#endif // SERVICESCATALOGCONTROLLER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,122 +0,0 @@
#ifndef SUBSCRIPTIONCONTROLLER_H
#define SUBSCRIPTIONCONTROLLER_H
#include <QJsonObject>
#include <QByteArray>
#include <QFuture>
#include <QList>
#include <QVariantMap>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
class ServersController;
class SubscriptionController
{
public:
struct ProtocolData
{
QString certRequest;
QString certPrivKey;
QString wireGuardClientPrivKey;
QString wireGuardClientPubKey;
QString xrayUuid;
};
struct GatewayRequestData
{
QString osVersion;
QString appVersion;
QString appLanguage;
QString installationUuid;
QString userCountryCode;
QString serverCountryCode;
QString serviceType;
QString serviceProtocol;
QJsonObject authData;
QJsonObject toJsonObject() const;
};
explicit SubscriptionController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository);
ProtocolData generateProtocolData(const QString &protocol);
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
ErrorCode fillServerConfig(const QJsonObject &serverConfigJson, ServerConfig &serverConfig);
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
ServerConfig &serverConfig);
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email,
ServerConfig &serverConfig);
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &transactionId, bool isTestPurchase,
ServerConfig &serverConfig,
int *duplicateServerIndex = nullptr);
ErrorCode updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent);
ErrorCode deactivateDevice(int serverIndex, bool isRemoveEvent);
ErrorCode deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
ErrorCode exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig);
ErrorCode revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
ErrorCode updateServiceFromTelegram(int serverIndex);
ErrorCode prepareVpnKeyExport(int serverIndex, QString &vpnKey);
ErrorCode validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers);
void removeApiConfig(int serverIndex);
void setCurrentProtocol(int serverIndex, const QString &protocolName);
bool isVlessProtocol(int serverIndex) const;
ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo);
QFuture<QPair<ErrorCode, QString>> getRenewalLink(int serverIndex);
struct AppStoreRestoreResult
{
bool hasInstalledConfig = false;
bool duplicateConfigAlreadyPresent = false;
int duplicateCount = 0;
int duplicateServerIndex = -1;
ErrorCode errorCode = ErrorCode::NoError;
};
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId,
ServerConfig &serverConfig,
int *duplicateServerIndex = nullptr);
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol);
private:
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
bool isApiKeyExpired(int serverIndex) const;
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
const ProtocolData &protocolData, QJsonObject &serverConfigJson);
void updateApiConfigInJson(QJsonObject &serverConfigJson, const QString &serviceType,
const QString &serviceProtocol, const QString &userCountryCode,
const QByteArray &apiResponseBody);
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
};
#endif // SUBSCRIPTIONCONTROLLER_H

View File

@@ -1,70 +0,0 @@
#include "appSplitTunnelingController.h"
AppSplitTunnelingController::AppSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository)
: m_appSettingsRepository(appSettingsRepository)
{
m_currentRouteMode = m_appSettingsRepository->appsRouteMode();
if (m_currentRouteMode == AppsRouteMode::VpnAllApps) { // for old split tunneling configs
m_currentRouteMode = AppsRouteMode::VpnAllExceptApps;
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
m_appSettingsRepository->setAppsRouteMode(AppsRouteMode::VpnAllExceptApps);
} else {
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
}
}
bool AppSplitTunnelingController::addApp(const amnezia::InstalledAppInfo &appInfo)
{
if (m_apps.contains(appInfo)) {
return false;
}
m_apps.append(appInfo);
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
return true;
}
void AppSplitTunnelingController::removeApp(int index)
{
if (index < 0 || index >= m_apps.size()) {
return;
}
m_apps.removeAt(index);
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
}
void AppSplitTunnelingController::clearAppsList()
{
m_apps.clear();
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
}
void AppSplitTunnelingController::setRouteMode(AppsRouteMode routeMode)
{
m_currentRouteMode = routeMode;
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
m_appSettingsRepository->setAppsRouteMode(routeMode);
}
void AppSplitTunnelingController::toggleSplitTunneling(bool enabled)
{
m_appSettingsRepository->setAppsSplitTunnelingEnabled(enabled);
}
AppsRouteMode AppSplitTunnelingController::getRouteMode() const
{
return m_currentRouteMode;
}
bool AppSplitTunnelingController::isSplitTunnelingEnabled() const
{
return m_appSettingsRepository->isAppsSplitTunnelingEnabled();
}
QVector<amnezia::InstalledAppInfo> AppSplitTunnelingController::getApps() const
{
return m_apps;
}

View File

@@ -1,32 +0,0 @@
#ifndef APPSPLITTUNNELINGCONTROLLER_H
#define APPSPLITTUNNELINGCONTROLLER_H
#include <QVector>
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h"
class AppSplitTunnelingController
{
public:
explicit AppSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository);
bool addApp(const amnezia::InstalledAppInfo &appInfo);
void removeApp(int index);
void clearAppsList();
void setRouteMode(AppsRouteMode routeMode);
void toggleSplitTunneling(bool enabled);
AppsRouteMode getRouteMode() const;
bool isSplitTunnelingEnabled() const;
QVector<amnezia::InstalledAppInfo> getApps() const;
private:
SecureAppSettingsRepository* m_appSettingsRepository;
AppsRouteMode m_currentRouteMode;
QVector<amnezia::InstalledAppInfo> m_apps;
};
#endif // APPSPLITTUNNELINGCONTROLLER_H

View File

@@ -1,183 +0,0 @@
#include "connectionController.h"
#include <QJsonDocument>
#include "core/configurators/configuratorBase.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/utilities.h"
#include "core/utils/networkUtilities.h"
#include "version.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h"
using namespace amnezia;
using namespace ProtocolUtils;
ConnectionController::ConnectionController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
VpnConnection* vpnConnection,
QObject* parent)
: QObject(parent),
m_serversRepository(serversRepository),
m_appSettingsRepository(appSettingsRepository),
m_vpnConnection(vpnConnection)
{
connect(m_vpnConnection, &VpnConnection::connectionStateChanged, this, &ConnectionController::connectionStateChanged);
connect(this, &ConnectionController::openConnectionRequested, m_vpnConnection, &VpnConnection::connectToVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::closeConnectionRequested, m_vpnConnection, &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::setConnectionStateRequested, m_vpnConnection, &VpnConnection::setConnectionState, Qt::QueuedConnection);
connect(this, &ConnectionController::killSwitchModeChangedRequested, m_vpnConnection, &VpnConnection::onKillSwitchModeChanged, Qt::QueuedConnection);
#ifdef Q_OS_ANDROID
connect(this, &ConnectionController::restoreConnectionRequested, m_vpnConnection, &VpnConnection::restoreConnection, Qt::QueuedConnection);
#endif
}
bool ConnectionController::isConnected() const
{
return m_vpnConnection && m_vpnConnection->connectionState() == Vpn::ConnectionState::Connected;
}
void ConnectionController::setConnectionState(Vpn::ConnectionState state)
{
if (m_vpnConnection) {
emit setConnectionStateRequested(state);
}
}
ErrorCode ConnectionController::prepareConnection(int serverIndex,
QJsonObject& vpnConfiguration,
DockerContainer& container)
{
if (!isServiceReady()) {
return ErrorCode::AmneziaServiceNotRunning;
}
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
container = serverConfigModel.defaultContainer();
if (!isContainerSupported(container)) {
return ErrorCode::NotSupportedOnThisPlatform;
}
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container);
auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container);
return ErrorCode::NoError;
}
ErrorCode ConnectionController::openConnection(int serverIndex)
{
QJsonObject vpnConfiguration;
DockerContainer container;
ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
return ErrorCode::NoError;
}
void ConnectionController::closeConnection()
{
if (m_vpnConnection) {
emit closeConnectionRequested();
}
}
#ifdef Q_OS_ANDROID
void ConnectionController::restoreConnection()
{
if (m_vpnConnection) {
emit restoreConnectionRequested();
}
}
#endif
void ConnectionController::onKillSwitchModeChanged(bool enabled)
{
if (m_vpnConnection) {
emit killSwitchModeChangedRequested(enabled);
}
}
ErrorCode ConnectionController::lastConnectionError() const
{
return m_vpnConnection->lastError();
}
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig,
const ContainerConfig &containerConfig,
DockerContainer container)
{
QJsonObject vpnConfiguration {};
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return vpnConfiguration;
}
Proto proto = ContainerUtils::defaultProtocol(container);
ConnectionSettings connectionSettings = {
{ dns.first, dns.second },
serverConfig.isApiConfig(),
{
m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
m_appSettingsRepository->routeMode()
}
};
auto configurator = ConfiguratorBase::create(proto, nullptr);
ProtocolConfig processedConfig = configurator->processConfigWithLocalSettings(connectionSettings,
containerConfig.protocolConfig);
QJsonObject vpnConfigData = processedConfig.getClientConfigJson();
if (ContainerUtils::isAwgContainer(container) || container == DockerContainer::WireGuard) {
if (vpnConfigData[configKey::mtu].toString().isEmpty()) {
vpnConfigData[configKey::mtu] =
ContainerUtils::isAwgContainer(container) ? protocols::awg::defaultMtu :
protocols::wireguard::defaultMtu;
}
}
vpnConfiguration.insert(ProtocolUtils::key_proto_config_data(proto), vpnConfigData);
vpnConfiguration[configKey::vpnProto] = ProtocolUtils::protoToString(proto);
vpnConfiguration[configKey::dns1] = dns.first;
vpnConfiguration[configKey::dns2] = dns.second;
vpnConfiguration[configKey::hostName] = serverConfig.hostName();
vpnConfiguration[configKey::description] = serverConfig.description();
vpnConfiguration[configKey::configVersion] = serverConfig.configVersion();
return vpnConfiguration;
}
bool ConnectionController::isServiceReady() const
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
return Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true);
#else
return true;
#endif
}
bool ConnectionController::isContainerSupported(DockerContainer container) const
{
return ContainerUtils::isSupportedByCurrentPlatform(container);
}

View File

@@ -1,78 +0,0 @@
#ifndef CONNECTIONCONTROLLER_H
#define CONNECTIONCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QPair>
#include <memory>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/protocols/vpnProtocol.h"
#include "vpnConnection.h"
using namespace amnezia;
class ConnectionController : public QObject
{
Q_OBJECT
public:
explicit ConnectionController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
VpnConnection* vpnConnection,
QObject* parent = nullptr);
~ConnectionController() = default;
ErrorCode prepareConnection(int serverIndex,
QJsonObject& vpnConfiguration,
DockerContainer& container);
ErrorCode openConnection(int serverIndex);
void closeConnection();
#ifdef Q_OS_ANDROID
void restoreConnection();
#endif
void onKillSwitchModeChanged(bool enabled);
ErrorCode lastConnectionError() const;
bool isConnected() const;
void setConnectionState(Vpn::ConnectionState state);
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig,
const ContainerConfig &containerConfig,
DockerContainer container);
bool isServiceReady() const;
bool isContainerSupported(DockerContainer container) const;
signals:
void connectionStateChanged(Vpn::ConnectionState state);
void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
void closeConnectionRequested();
void setConnectionStateRequested(Vpn::ConnectionState state);
void killSwitchModeChangedRequested(bool enabled);
#ifdef Q_OS_ANDROID
void restoreConnectionRequested();
#endif
private:
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
VpnConnection* m_vpnConnection;
};
#endif

View File

@@ -2,18 +2,9 @@
#include <QDirIterator>
#include <QTranslator>
#include <QTimer>
#include "core/utils/selfhosted/sshSession.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/selfhosted/importController.h"
#include "core/controllers/coreSignalHandlers.h"
#include "core/models/serverConfig.h"
#include "logger.h"
#include "secureQSettings.h"
#if defined(Q_OS_ANDROID)
#include "core/utils/installedAppsImageProvider.h"
#include "core/installedAppsImageProvider.h"
#include "platforms/android/android_controller.h"
#endif
@@ -22,196 +13,149 @@
#include <AmneziaVPN-Swift.h>
#endif
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
QQmlApplicationEngine *engine, QObject *parent)
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
{
initRepositories();
initCoreControllers();
initModels();
initControllers();
initSignalHandlers();
initAndroidController();
initAppleController();
initLogging();
m_translator = new QTranslator(this);
if (m_appSettingsRepository) {
updateTranslator(m_appSettingsRepository->getAppLanguage());
}
}
initNotificationHandler();
void CoreController::setQmlContextProperty(const QString &name, QObject *value)
{
if (m_engine) {
m_engine->rootContext()->setContextProperty(name, value);
}
m_translator.reset(new QTranslator());
updateTranslator(m_settings->getAppLanguage());
}
void CoreController::initModels()
{
m_containersModel = new ContainersModel(this);
setQmlContextProperty("ContainersModel", m_containersModel);
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_defaultServerContainersModel = new ContainersModel(this);
setQmlContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel);
m_defaultServerContainersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
m_serversModel = new ServersModel(this);
setQmlContextProperty("ServersModel", m_serversModel);
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
m_languageModel = new LanguageModel(this);
setQmlContextProperty("LanguageModel", m_languageModel);
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
m_ipSplitTunnelingModel = new IpSplitTunnelingModel(this);
setQmlContextProperty("IpSplitTunnelingModel", m_ipSplitTunnelingModel);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_allowedDnsModel = new AllowedDnsModel(this);
setQmlContextProperty("AllowedDnsModel", m_allowedDnsModel);
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
m_appSplitTunnelingModel = new AppSplitTunnelingModel(this);
setQmlContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel);
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
m_protocolsModel = new ProtocolsModel(this);
setQmlContextProperty("ProtocolsModel", m_protocolsModel);
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
m_openVpnConfigModel = new OpenVpnConfigModel(this);
setQmlContextProperty("OpenVpnConfigModel", m_openVpnConfigModel);
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
m_wireGuardConfigModel = new WireGuardConfigModel(this);
setQmlContextProperty("WireGuardConfigModel", m_wireGuardConfigModel);
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_awgConfigModel = new AwgConfigModel(this);
setQmlContextProperty("AwgConfigModel", m_awgConfigModel);
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_xrayConfigModel = new XrayConfigModel(this);
setQmlContextProperty("XrayConfigModel", m_xrayConfigModel);
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_torConfigModel = new TorConfigModel(this);
setQmlContextProperty("TorConfigModel", m_torConfigModel);
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
m_xrayConfigModel.reset(new XrayConfigModel(this));
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel = new Ikev2ConfigModel(this);
setQmlContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel);
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel = new SftpConfigModel(this);
setQmlContextProperty("SftpConfigModel", m_sftpConfigModel);
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
m_clientManagementModel = new ClientManagementModel(this);
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
m_apiServicesModel = new ApiServicesModel(this);
setQmlContextProperty("ApiServicesModel", m_apiServicesModel);
m_apiServicesModel.reset(new ApiServicesModel(this));
m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
m_apiCountryModel = new ApiCountryModel(this);
setQmlContextProperty("ApiCountryModel", m_apiCountryModel);
m_apiCountryModel.reset(new ApiCountryModel(this));
m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
m_apiSubscriptionPlansModel = new ApiSubscriptionPlansModel(this);
setQmlContextProperty("ApiSubscriptionPlansModel", m_apiSubscriptionPlansModel);
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
m_apiBenefitsModel = new ApiBenefitsModel(this);
setQmlContextProperty("ApiBenefitsModel", m_apiBenefitsModel);
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
m_apiAccountInfoModel = new ApiAccountInfoModel(this);
setQmlContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel);
m_apiDevicesModel = new ApiDevicesModel(this);
setQmlContextProperty("ApiDevicesModel", m_apiDevicesModel);
m_newsModel = new NewsModel(m_appSettingsRepository, this);
setQmlContextProperty("NewsModel", m_newsModel);
}
void CoreController::initRepositories()
{
m_serversRepository = new SecureServersRepository(m_settings, this);
m_appSettingsRepository = new SecureAppSettingsRepository(m_settings, this);
if (m_vpnConnection) {
m_vpnConnection->setRepositories(m_serversRepository, m_appSettingsRepository);
}
}
void CoreController::initCoreControllers()
{
m_serversController = new ServersController(m_serversRepository, m_appSettingsRepository, this);
m_appSplitTunnelingController = new AppSplitTunnelingController(m_appSettingsRepository);
m_usersController = new UsersController(m_serversRepository, this);
m_ipSplitTunnelingController = new IpSplitTunnelingController(m_appSettingsRepository, this);
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
m_newsController = new NewsController(m_appSettingsRepository, m_serversController);
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
m_exportController = new ExportController(m_serversRepository, m_appSettingsRepository, this);
m_importCoreController = new ImportController(m_serversRepository, m_appSettingsRepository, this);
m_connectionController = new ConnectionController(m_serversRepository, m_appSettingsRepository, m_vpnConnection.get(), this);
m_settingsController = new SettingsController(m_serversRepository, m_appSettingsRepository, this);
m_newsModel.reset(new NewsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
}
void CoreController::initControllers()
{
m_connectionUiController = new ConnectionUiController(m_connectionController, m_serversController, this);
setQmlContextProperty("ConnectionController", m_connectionUiController);
m_connectionController.reset(
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
if (m_engine) {
m_focusController = new FocusController(m_engine, this);
setQmlContextProperty("FocusController", m_focusController);
}
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel,
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel,
#endif
m_sftpConfigModel, m_socks5ConfigModel, this);
setQmlContextProperty("InstallController", m_installUiController);
m_focusController.reset(new FocusController(m_engine, this));
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
m_importController = new ImportUiController(m_importCoreController, this);
setQmlContextProperty("ImportController", m_importController);
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
m_exportUiController = new ExportUiController(m_exportController, this);
setQmlContextProperty("ExportController", m_exportUiController);
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
m_languageUiController = new LanguageUiController(m_settingsController, m_languageModel, this);
setQmlContextProperty("LanguageUiController", m_languageUiController);
connect(m_installController.get(), &InstallController::profileCleared,
m_protocolsModel.get(), &ProtocolsModel::updateModel);
m_settingsUiController = new SettingsUiController(m_settingsController, m_serversController, m_languageUiController, this);
setQmlContextProperty("SettingsController", m_settingsUiController);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_pageController = new PageController(m_serversController, m_settingsController, this);
setQmlContextProperty("PageController", m_pageController);
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_serversUiController = new ServersUiController(m_serversController, m_settingsController, m_serversModel, m_containersModel, m_defaultServerContainersModel, this);
setQmlContextProperty("ServersUiController", m_serversUiController);
m_settingsController.reset(
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
m_ipSplitTunnelingUiController = new IpSplitTunnelingUiController(m_ipSplitTunnelingController, m_ipSplitTunnelingModel, this);
setQmlContextProperty("IpSplitTunnelingController", m_ipSplitTunnelingUiController);
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_allowedDnsUiController = new AllowedDnsUiController(m_allowedDnsController, m_allowedDnsModel, this);
setQmlContextProperty("AllowedDnsController", m_allowedDnsUiController);
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
m_appSplitTunnelingUiController = new AppSplitTunnelingUiController(m_appSplitTunnelingController, m_appSplitTunnelingModel, this);
setQmlContextProperty("AppSplitTunnelingController", m_appSplitTunnelingUiController);
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
m_systemController = new SystemController(this);
setQmlContextProperty("SystemController", m_systemController);
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
m_apiSettingsController.reset(
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
m_subscriptionUiController = new SubscriptionUiController(m_serversController, m_apiServicesModel, m_servicesCatalogController, m_subscriptionController,
m_apiSubscriptionPlansModel, m_apiBenefitsModel, m_apiAccountInfoModel,
m_apiCountryModel, m_apiDevicesModel, m_settingsController, this);
setQmlContextProperty("SubscriptionUiController", m_subscriptionUiController);
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
}
void CoreController::initAndroidController()
@@ -220,16 +164,33 @@ void CoreController::initAndroidController()
if (!AndroidController::initLogging()) {
qFatal("Android logging initialization failed");
}
AndroidController::instance()->setSaveLogs(m_appSettingsRepository->isSaveLogs());
AndroidController::instance()->setScreenshotsEnabled(m_appSettingsRepository->isScreenshotsEnabled());
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qFatal("Android controller initialization failed");
}
if (m_engine) {
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data);
data.clear();
emit m_pageController->goToPageViewConfig();
});
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
#endif
}
@@ -237,36 +198,63 @@ void CoreController::initAppleController()
{
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_appSettingsRepository->isScreenshotsEnabled()); });
#endif
}
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data);
emit m_pageController->goToPageViewConfig();
});
void CoreController::initLogging()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
bool enabled = m_appSettingsRepository->isSaveLogs();
if (enabled) {
if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed";
}
}
Logger::setServiceLogsEnabled(enabled);
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
emit m_pageController->goToPageHome();
m_pageController->goToPageSettingsBackup();
emit m_settingsController->importBackupFromOutside(filePath);
});
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
#endif
}
void CoreController::initSignalHandlers()
{
m_signalHandlers = new CoreSignalHandlers(this, this);
m_signalHandlers->initAllHandlers();
// Trigger initial update after handlers are connected
m_serversUiController->updateModel();
initErrorMessagesHandler();
initApiCountryModelUpdateHandler();
initContainerModelUpdateHandler();
initAdminConfigRevokedHandler();
initPassphraseRequestHandler();
initTranslationsUpdatedHandler();
initAutoConnectHandler();
initAmneziaDnsToggledHandler();
initPrepareConfigHandler();
initStrictKillSwitchHandler();
}
void CoreController::initNotificationHandler()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState);
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection));
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
}
void CoreController::updateTranslator(const QLocale &locale)
{
if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator);
QCoreApplication::removeTranslator(m_translator.get());
}
QStringList availableTranslations;
@@ -287,31 +275,115 @@ void CoreController::updateTranslator(const QLocale &locale)
}
if (m_translator->load(strFileName)) {
QCoreApplication::installTranslator(m_translator);
} else {
if (m_translator->load(QString(":/translations/amneziavpn_en.qm"))) {
QCoreApplication::installTranslator(m_translator);
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
}
} else {
m_settings->setAppLanguage(QLocale::English);
}
if (m_engine) {
m_engine->retranslate();
}
m_engine->retranslate();
emit translationsUpdated();
if (m_languageUiController) {
emit websiteUrlChanged(m_languageUiController->getCurrentSiteUrl());
}
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
}
void CoreController::initErrorMessagesHandler()
{
connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
emit m_pageController->showErrorMessage(errorCode);
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_apiConfigsController.get(), &ApiConfigsController::errorOccurred, m_pageController.get(),
qOverload<ErrorCode>(&PageController::showErrorMessage));
}
void CoreController::setQmlRoot()
{
if (m_engine && m_systemController) {
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
}
void CoreController::initApiCountryModelUpdateHandler()
{
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
});
}
void CoreController::initContainerModelUpdateHandler()
{
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::gatewayStacksExpanded, this, [this]() {
if (m_serversModel->hasServersFromGatewayApi()) {
m_apiNewsController->fetchNews(false);
}
});
m_serversModel->resetModel();
}
void CoreController::initAdminConfigRevokedHandler()
{
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
&ServersModel::clearCachedProfile);
}
void CoreController::initPassphraseRequestHandler()
{
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
}
void CoreController::initTranslationsUpdatedHandler()
{
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &CoreController::updateTranslator);
connect(this, &CoreController::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
}
void CoreController::initAutoConnectHandler()
{
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
}
PageController* CoreController::pageController() const
void CoreController::initAmneziaDnsToggledHandler()
{
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
}
void CoreController::initPrepareConfigHandler()
{
connect(m_connectionController.get(), &ConnectionController::prepareConfig, this, [this]() {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
if (!m_apiConfigsController->isConfigValid()) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
if (!m_installController->isConfigValid()) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
m_connectionController->openConnection();
});
}
void CoreController::initStrictKillSwitchHandler()
{
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
&VpnConnection::onKillSwitchModeChanged);
}
QSharedPointer<PageController> CoreController::pageController() const
{
return m_pageController;
}
@@ -320,11 +392,9 @@ void CoreController::openConnectionByIndex(int serverIndex)
{
if (m_serversModel) {
m_serversModel->setProcessedServerIndex(serverIndex);
m_serversModel->setDefaultServerIndex(serverIndex);
}
if (m_serversController) {
m_serversController->setDefaultServerIndex(serverIndex);
}
m_connectionUiController->toggleConnection();
m_connectionController->toggleConnection();
}
void CoreController::importConfigFromData(const QString &data)

View File

@@ -6,208 +6,148 @@
#include <QThread>
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/utils/systemTrayNotificationHandler.h"
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/subscriptionUiController.h"
#include "ui/controllers/api/apiNewsUiController.h"
#include "ui/controllers/appSplitTunnelingUiController.h"
#include "ui/controllers/allowedDnsUiController.h"
#include "ui/controllers/connectionUiController.h"
#include "ui/controllers/selfhosted/exportUiController.h"
#include "core/controllers/selfhosted/exportController.h"
#include "ui/controllers/qml/focusController.h"
#include "ui/controllers/importUiController.h"
#include "core/controllers/selfhosted/importController.h"
#include "ui/controllers/selfhosted/installUiController.h"
#include "ui/controllers/qml/pageController.h"
#include "ui/controllers/settingsUiController.h"
#include "ui/controllers/serversUiController.h"
#include "ui/controllers/ipSplitTunnelingUiController.h"
#include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/api/apiNewsController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/focusController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/languageUiController.h"
#include "ui/controllers/api/servicesCatalogUiController.h"
#include "core/controllers/serversController.h"
#include "core/controllers/selfhosted/usersController.h"
#include "core/controllers/appSplitTunnelingController.h"
#include "core/controllers/ipSplitTunnelingController.h"
#include "core/controllers/allowedDnsController.h"
#include "core/controllers/api/servicesCatalogController.h"
#include "core/controllers/api/subscriptionController.h"
#include "core/controllers/api/newsController.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/connectionController.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "secureQSettings.h"
#include "ui/models/allowedDnsModel.h"
#include "ui/models/containersModel.h"
#include "ui/models/allowed_dns_model.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiBenefitsModel.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/api/apiSubscriptionPlansModel.h"
#include "ui/models/appSplitTunnelingModel.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols/xrayConfigModel.h"
#include "ui/models/protocolsModel.h"
#include "ui/models/services/torConfigModel.h"
#include "ui/models/serversModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/ipSplitTunnelingModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/newsModel.h"
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/utils/notificationHandler.h"
#include "ui/notificationhandler.h"
#endif
class CoreSignalHandlers;
class TestMultipleImports;
class TestAdminSelfHostedExport;
class TestServerEdit;
class TestDefaultServerChange;
class TestServerEdgeCases;
class TestSignalOrder;
class TestServersModelSync;
class TestGatewayStacks;
class TestComplexOperations;
class TestSettingsSignals;
class TestUiServersModelAndController;
class TestSelfHostedServerSetup;
class CoreController : public QObject
{
Q_OBJECT
friend class CoreSignalHandlers;
friend class TestMultipleImports;
friend class TestAdminSelfHostedExport;
friend class TestServerEdit;
friend class TestDefaultServerChange;
friend class TestServerEdgeCases;
friend class TestSignalOrder;
friend class TestServersModelSync;
friend class TestGatewayStacks;
friend class TestComplexOperations;
friend class TestSettingsSignals;
friend class TestUiServersModelAndController;
friend class TestSelfHostedServerSetup;
public:
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
QQmlApplicationEngine *engine, QObject *parent = nullptr);
PageController* pageController() const;
QSharedPointer<PageController> pageController() const;
void setQmlRoot();
void openConnectionByIndex(int serverIndex);
void importConfigFromData(const QString &data);
void updateTranslator(const QLocale &locale);
signals:
void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private:
void initRepositories();
void initCoreControllers();
void initModels();
void initControllers();
void initAndroidController();
void initAppleController();
void initLogging();
void initSignalHandlers();
void setQmlContextProperty(const QString &name, QObject *value);
void initNotificationHandler();
void updateTranslator(const QLocale &locale);
void initErrorMessagesHandler();
void initApiCountryModelUpdateHandler();
void initContainerModelUpdateHandler();
void initAdminConfigRevokedHandler();
void initPassphraseRequestHandler();
void initTranslationsUpdatedHandler();
void initAutoConnectHandler();
void initAmneziaDnsToggledHandler();
void initPrepareConfigHandler();
void initStrictKillSwitchHandler();
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
SecureQSettings* m_settings;
std::shared_ptr<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QTranslator* m_translator;
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
QSharedPointer<QTranslator> m_translator;
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
NotificationHandler* m_notificationHandler;
QScopedPointer<NotificationHandler> m_notificationHandler;
#endif
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
ConnectionUiController* m_connectionUiController;
FocusController* m_focusController;
PageController* m_pageController;
InstallUiController* m_installUiController;
ImportUiController* m_importController;
ImportController* m_importCoreController;
ExportUiController* m_exportUiController;
SettingsUiController* m_settingsUiController;
ServersUiController* m_serversUiController;
IpSplitTunnelingUiController* m_ipSplitTunnelingUiController;
SystemController* m_systemController;
AppSplitTunnelingUiController* m_appSplitTunnelingUiController;
AllowedDnsUiController* m_allowedDnsUiController;
LanguageUiController* m_languageUiController;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<FocusController> m_focusController;
QSharedPointer<PageController> m_pageController; // TODO
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<AllowedDnsController> m_allowedDnsController;
SubscriptionUiController* m_subscriptionUiController;
ApiNewsUiController* m_apiNewsUiController;
ServicesCatalogUiController* m_servicesCatalogUiController;
QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController;
QScopedPointer<ApiNewsController> m_apiNewsController;
ServersController* m_serversController;
UsersController* m_usersController;
AppSplitTunnelingController* m_appSplitTunnelingController;
IpSplitTunnelingController* m_ipSplitTunnelingController;
AllowedDnsController* m_allowedDnsController;
ServicesCatalogController* m_servicesCatalogController;
SubscriptionController* m_subscriptionController;
NewsController* m_newsController;
InstallController* m_installController;
ExportController* m_exportController;
ConnectionController* m_connectionController;
SettingsController* m_settingsController;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<NewsModel> m_newsModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
ContainersModel* m_containersModel;
ContainersModel* m_defaultServerContainersModel;
ServersModel* m_serversModel;
LanguageModel* m_languageModel;
ProtocolsModel* m_protocolsModel;
IpSplitTunnelingModel* m_ipSplitTunnelingModel;
NewsModel* m_newsModel;
AllowedDnsModel* m_allowedDnsModel;
AppSplitTunnelingModel* m_appSplitTunnelingModel;
ClientManagementModel* m_clientManagementModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
QSharedPointer<ApiCountryModel> m_apiCountryModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
ApiServicesModel* m_apiServicesModel;
ApiSubscriptionPlansModel* m_apiSubscriptionPlansModel;
ApiBenefitsModel* m_apiBenefitsModel;
ApiCountryModel* m_apiCountryModel;
ApiAccountInfoModel* m_apiAccountInfoModel;
ApiDevicesModel* m_apiDevicesModel;
OpenVpnConfigModel* m_openVpnConfigModel;
XrayConfigModel* m_xrayConfigModel;
TorConfigModel* m_torConfigModel;
WireGuardConfigModel* m_wireGuardConfigModel;
AwgConfigModel* m_awgConfigModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
Ikev2ConfigModel* m_ikev2ConfigModel;
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
SftpConfigModel* m_sftpConfigModel;
Socks5ProxyConfigModel* m_socks5ConfigModel;
CoreSignalHandlers* m_signalHandlers;
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
};
#endif // CORECONTROLLER_H

View File

@@ -1,412 +0,0 @@
#include "coreSignalHandlers.h"
#include <QTimer>
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/controllers/coreController.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "vpnConnection.h"
#include "ui/controllers/qml/pageController.h"
#include "ui/controllers/connectionUiController.h"
#include "ui/controllers/settingsUiController.h"
#include "ui/controllers/serversUiController.h"
#include "ui/controllers/ipSplitTunnelingUiController.h"
#include "ui/controllers/allowedDnsUiController.h"
#include "ui/controllers/appSplitTunnelingUiController.h"
#include "ui/controllers/languageUiController.h"
#include "ui/controllers/selfhosted/installUiController.h"
#include "ui/controllers/importUiController.h"
#include "ui/controllers/api/subscriptionUiController.h"
#include "ui/models/serversModel.h"
#include "core/controllers/serversController.h"
#include "core/controllers/ipSplitTunnelingController.h"
#include "core/controllers/appSplitTunnelingController.h"
#include "core/controllers/selfhosted/usersController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/selfhosted/exportController.h"
#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"
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/utils/notificationHandler.h"
#include "ui/utils/systemTrayNotificationHandler.h"
#endif
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#include <AmneziaVPN-Swift.h>
#endif
CoreSignalHandlers::CoreSignalHandlers(CoreController* coreController, QObject* parent)
: QObject(parent),
m_coreController(coreController)
{
}
void CoreSignalHandlers::initAllHandlers()
{
initErrorMessagesHandler();
initSettingsSplitTunnelingHandler();
initInstallControllerHandler();
initExportControllerHandler();
initImportControllerHandler();
initApiCountryModelUpdateHandler();
initSubscriptionRefreshHandler();
initContainerModelUpdateHandler();
initAdminConfigRevokedHandler();
initPassphraseRequestHandler();
initTranslationsUpdatedHandler();
initLanguageHandler();
initAutoConnectHandler();
initAmneziaDnsToggledHandler();
initServersModelUpdateHandler();
initClientManagementModelUpdateHandler();
initSitesModelUpdateHandler();
initAllowedDnsModelUpdateHandler();
initAppSplitTunnelingModelUpdateHandler();
initPrepareConfigHandler();
initStrictKillSwitchHandler();
initAndroidSettingsHandler();
initAndroidConnectionHandler();
initIosImportHandler();
initIosSettingsHandler();
initNotificationHandler();
}
void CoreSignalHandlers::initErrorMessagesHandler()
{
connect(m_coreController->m_connectionUiController, &ConnectionUiController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
emit m_coreController->m_pageController->showErrorMessage(errorCode);
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::errorOccurred, m_coreController->m_pageController,
qOverload<ErrorCode>(&PageController::showErrorMessage));
connect(m_coreController->m_settingsUiController, &SettingsUiController::errorOccurred, m_coreController->m_pageController,
qOverload<ErrorCode>(&PageController::showErrorMessage));
}
void CoreSignalHandlers::initSettingsSplitTunnelingHandler()
{
connect(m_coreController->m_settingsController, &SettingsController::siteSplitTunnelingRouteModeChanged, this, [this](RouteMode mode) {
m_coreController->m_ipSplitTunnelingController->setRouteMode(mode);
});
connect(m_coreController->m_settingsController, &SettingsController::siteSplitTunnelingToggled, this, [this](bool enabled) {
m_coreController->m_ipSplitTunnelingController->toggleSplitTunneling(enabled);
});
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingRouteModeChanged, this, [this](AppsRouteMode mode) {
m_coreController->m_appSplitTunnelingController->setRouteMode(mode);
});
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingToggled, this, [this](bool enabled) {
m_coreController->m_appSplitTunnelingController->toggleSplitTunneling(enabled);
});
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingClearAppsList, this, [this]() {
m_coreController->m_appSplitTunnelingController->clearAppsList();
});
}
void CoreSignalHandlers::initInstallControllerHandler()
{
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
connect(m_coreController->m_installUiController, &InstallUiController::currentContainerUpdated, m_coreController->m_connectionUiController,
&ConnectionUiController::onCurrentContainerUpdated);
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
m_coreController->m_installUiController, [this](int index) {
if (index >= 0) {
m_coreController->m_installUiController->clearProcessedServerCredentials();
}
});
}
void CoreSignalHandlers::initExportControllerHandler()
{
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
});
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
[this](int serverIndex, DockerContainer container) {
m_coreController->m_usersController->updateClients(serverIndex, container);
});
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
[this](int serverIndex, int row, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, row, container);
});
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
[this](int serverIndex, int row, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container);
});
}
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;
m_coreController->m_serversController->setDefaultServerIndex(newServerIndex);
if (m_coreController->m_serversUiController) {
m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex);
}
}
});
}
void CoreSignalHandlers::initApiCountryModelUpdateHandler()
{
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex();
if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) {
return;
}
ServerConfig server = m_coreController->m_serversRepository->server(processedIndex);
QJsonArray availableCountries;
QString serverCountryCode;
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (apiV2) {
availableCountries = apiV2->apiConfig.availableCountries;
serverCountryCode = apiV2->apiConfig.serverCountryCode;
}
}
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
});
}
void CoreSignalHandlers::initSubscriptionRefreshHandler()
{
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex();
if (defaultServerIndex >= 0) {
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, false);
}
});
}
void CoreSignalHandlers::initContainerModelUpdateHandler()
{
connect(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded, this, [this]() {
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
m_coreController->m_apiNewsUiController->fetchNews(false);
}
});
}
void CoreSignalHandlers::initAdminConfigRevokedHandler()
{
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
[this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container);
});
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
});
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController,
&ServersController::clearCachedProfile);
}
void CoreSignalHandlers::initPassphraseRequestHandler()
{
connect(m_coreController->m_installUiController, &InstallUiController::passphraseRequestStarted, m_coreController->m_pageController,
&PageController::showPassphraseRequestDrawer);
connect(m_coreController->m_pageController, &PageController::passphraseRequestDrawerClosed, m_coreController->m_installUiController,
&InstallUiController::setEncryptedPassphrase);
}
void CoreSignalHandlers::initTranslationsUpdatedHandler()
{
connect(m_coreController->m_languageUiController, &LanguageUiController::updateTranslations, m_coreController, &CoreController::updateTranslator);
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_languageUiController, &LanguageUiController::translationsUpdated);
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_connectionUiController, &ConnectionUiController::onTranslationsUpdated);
}
void CoreSignalHandlers::initLanguageHandler()
{
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appLanguageChanged, m_coreController->m_languageUiController, &LanguageUiController::onAppLanguageChanged);
connect(m_coreController->m_settingsUiController, &SettingsUiController::resetLanguageToSystem, m_coreController->m_languageUiController, [this]() {
m_coreController->m_languageUiController->changeLanguage(m_coreController->m_languageUiController->getSystemLanguageEnum());
});
}
void CoreSignalHandlers::initAutoConnectHandler()
{
if (m_coreController->m_settingsUiController->isAutoConnectEnabled() && m_coreController->m_serversController->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
}
}
void CoreSignalHandlers::initAmneziaDnsToggledHandler()
{
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::useAmneziaDnsChanged, m_coreController->m_serversUiController, &ServersUiController::updateModel);
}
void CoreSignalHandlers::initServersModelUpdateHandler()
{
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished,
m_coreController->m_serversUiController, &ServersUiController::updateModel);
}
void CoreSignalHandlers::initClientManagementModelUpdateHandler()
{
connect(m_coreController->m_usersController, &UsersController::clientsUpdated,
m_coreController->m_clientManagementModel, &ClientManagementModel::updateModel);
connect(m_coreController->m_usersController, &UsersController::clientRenamed,
m_coreController->m_clientManagementModel, &ClientManagementModel::updateClientName);
}
void CoreSignalHandlers::initSitesModelUpdateHandler()
{
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::sitesChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::sitesSplitTunnelingEnabledChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::routeModeChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
}
void CoreSignalHandlers::initAllowedDnsModelUpdateHandler()
{
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::allowedDnsServersChanged, m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::updateModel);
}
void CoreSignalHandlers::initAppSplitTunnelingModelUpdateHandler()
{
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsSplitTunnelingEnabledChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsRouteModeChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
}
void CoreSignalHandlers::initPrepareConfigHandler()
{
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing);
m_coreController->m_subscriptionUiController->validateConfig();
});
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) {
if (!isValid) {
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
m_coreController->m_installUiController->validateConfig();
});
connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) {
if (!isValid) {
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
m_coreController->m_connectionUiController->openConnection();
});
}
void CoreSignalHandlers::initStrictKillSwitchHandler()
{
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
&ConnectionController::onKillSwitchModeChanged);
}
void CoreSignalHandlers::initAndroidSettingsHandler()
{
#ifdef Q_OS_ANDROID
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
#endif
}
void CoreSignalHandlers::initAndroidConnectionHandler()
{
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
m_coreController->m_connectionUiController->onConnectionStateChanged(state);
m_coreController->m_connectionController->restoreConnection();
});
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
emit m_coreController->m_pageController->goToPageHome();
m_coreController->m_importController->extractConfigFromData(data);
data.clear();
emit m_coreController->m_pageController->goToPageViewConfig();
});
#endif
}
void CoreSignalHandlers::initIosImportHandler()
{
#ifdef Q_OS_IOS
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
emit m_coreController->m_pageController->goToPageHome();
m_coreController->m_importController->extractConfigFromData(data);
emit m_coreController->m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
emit m_coreController->m_pageController->goToPageHome();
m_coreController->m_pageController->goToPageSettingsBackup();
emit m_coreController->m_settingsUiController->importBackupFromOutside(filePath);
});
#endif
}
void CoreSignalHandlers::initIosSettingsHandler()
{
#ifdef Q_OS_IOS
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
#endif
}
void CoreSignalHandlers::initNotificationHandler()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_coreController->m_notificationHandler = NotificationHandler::create(m_coreController);
connect(m_coreController->m_connectionController, &ConnectionController::connectionStateChanged, m_coreController->m_notificationHandler,
&NotificationHandler::setConnectionState);
connect(m_coreController->m_notificationHandler, &NotificationHandler::raiseRequested, m_coreController->m_pageController, &PageController::raiseMainWindow);
connect(m_coreController->m_notificationHandler, &NotificationHandler::connectRequested, m_coreController->m_connectionUiController,
static_cast<void (ConnectionUiController::*)()>(&ConnectionUiController::openConnection));
connect(m_coreController->m_notificationHandler, &NotificationHandler::disconnectRequested, m_coreController->m_connectionUiController,
&ConnectionUiController::closeConnection);
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_notificationHandler, &NotificationHandler::onTranslationsUpdated);
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
}

View File

@@ -1,48 +0,0 @@
#ifndef CORESIGNALHANDLERS_H
#define CORESIGNALHANDLERS_H
#include <QObject>
#include "core/controllers/coreController.h"
class CoreSignalHandlers : public QObject
{
Q_OBJECT
public:
explicit CoreSignalHandlers(CoreController* coreController, QObject* parent = nullptr);
void initAllHandlers();
private:
void initErrorMessagesHandler();
void initSettingsSplitTunnelingHandler();
void initInstallControllerHandler();
void initExportControllerHandler();
void initImportControllerHandler();
void initApiCountryModelUpdateHandler();
void initSubscriptionRefreshHandler();
void initContainerModelUpdateHandler();
void initAdminConfigRevokedHandler();
void initPassphraseRequestHandler();
void initTranslationsUpdatedHandler();
void initLanguageHandler();
void initAutoConnectHandler();
void initAmneziaDnsToggledHandler();
void initServersModelUpdateHandler();
void initClientManagementModelUpdateHandler();
void initSitesModelUpdateHandler();
void initAllowedDnsModelUpdateHandler();
void initAppSplitTunnelingModelUpdateHandler();
void initPrepareConfigHandler();
void initStrictKillSwitchHandler();
void initAndroidSettingsHandler();
void initAndroidConnectionHandler();
void initIosImportHandler();
void initIosSettingsHandler();
void initNotificationHandler();
CoreController* m_coreController;
};
#endif // CORESIGNALHANDLERS_H

View File

@@ -15,18 +15,27 @@
#include "QBlockCipher.h"
#include "QRsa.h"
#include "amneziaApplication.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/utilities.h"
#include "amnezia_application.h"
#include "core/api/apiUtils.h"
#include "core/networkUtilities.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP
#include "core/utils/ipcClient.h"
#include "core/ipcclient.h"
#endif
namespace
{
namespace configKey
{
constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv";
constexpr char aesSalt[] = "aes_salt";
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
@@ -35,13 +44,8 @@ namespace
constexpr int httpStatusCodeNotFound = 404;
constexpr int httpStatusCodeConflict = 409;
constexpr int httpStatusCodeNotImplemented = 501;
constexpr int httpStatusCodePaymentRequired = 402;
constexpr int httpStatusCodeUnprocessableEntity = 422;
constexpr QLatin1String unprocessableSubscriptionMessage("Failed to retrieve subscription information. Is it activated?");
constexpr int proxyStorageRequestTimeoutMsecs = 3000;
}
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
@@ -90,9 +94,9 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
encRequestData.salt = blockCipher.generatePrivateSalt(8);
QJsonObject keyPayload;
keyPayload[apiDefs::key::aesKey] = QString(encRequestData.key.toBase64());
keyPayload[apiDefs::key::aesIv] = QString(encRequestData.iv.toBase64());
keyPayload[apiDefs::key::aesSalt] = QString(encRequestData.salt.toBase64());
keyPayload[configKey::aesKey] = QString(encRequestData.key.toBase64());
keyPayload[configKey::aesIv] = QString(encRequestData.iv.toBase64());
keyPayload[configKey::aesSalt] = QString(encRequestData.salt.toBase64());
QByteArray encryptedKeyPayload;
QByteArray encryptedApiPayload;
@@ -124,8 +128,8 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
}
QJsonObject requestBody;
requestBody[apiDefs::key::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[apiDefs::key::apiPayload] = QString(encryptedApiPayload.toBase64());
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
encRequestData.requestBody = QJsonDocument(requestBody).toJson();
return encRequestData;
@@ -277,34 +281,23 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
QStringList primaryBaseUrls;
QStringList fallbackBaseUrls;
QStringList baseUrls;
if (m_isDevEnvironment) {
primaryBaseUrls = QString(DEV_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
primaryBaseUrls = QString(PROD_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
fallbackBaseUrls = QString(FALLBACK_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(primaryBaseUrls.begin(), primaryBaseUrls.end(), generator);
std::shuffle(fallbackBaseUrls.begin(), fallbackBaseUrls.end(), generator);
auto appendStorageUrls = [&serviceType, &userCountryCode](const QStringList &baseUrls, QStringList &target) {
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
target.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
}
}
for (const auto &baseUrl : baseUrls) {
target.push_back(baseUrl + "endpoints.json");
}
};
QStringList proxyStorageUrls;
appendStorageUrls(primaryBaseUrls, proxyStorageUrls);
appendStorageUrls(fallbackBaseUrls, proxyStorageUrls);
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)
+ ".json");
}
}
for (const auto &baseUrl : baseUrls)
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
getProxyUrlsAsync(proxyStorageUrls, 0, [this, encRequestData, endpoint, processResponse](const QStringList &proxyUrls) {
getProxyUrlAsync(proxyUrls, 0, [this, encRequestData, endpoint, processResponse](const QString &proxyUrl) {
@@ -331,48 +324,31 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
{
QNetworkRequest request;
request.setTransferTimeout(proxyStorageRequestTimeoutMsecs);
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList primaryBaseUrls;
QStringList fallbackBaseUrls;
QStringList baseUrls;
if (m_isDevEnvironment) {
primaryBaseUrls = QString(DEV_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
primaryBaseUrls = QString(PROD_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
fallbackBaseUrls = QString(FALLBACK_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(primaryBaseUrls.begin(), primaryBaseUrls.end(), generator);
std::shuffle(fallbackBaseUrls.begin(), fallbackBaseUrls.end(), generator);
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
auto appendStorageUrls = [&serviceType, &userCountryCode](const QStringList &baseUrls, QStringList &target) {
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
target.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
}
}
for (const auto &baseUrl : baseUrls) {
target.push_back(baseUrl + "endpoints.json");
}
};
QStringList proxyStorageUrls;
appendStorageUrls(primaryBaseUrls, proxyStorageUrls);
appendStorageUrls(fallbackBaseUrls, proxyStorageUrls);
if (proxyStorageUrls.empty()) {
qDebug() << "empty storage endpoint list";
return {};
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
}
}
for (const auto &baseUrl : baseUrls) {
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
}
for (const auto &proxyStorageUrl : proxyStorageUrls) {
@@ -436,14 +412,12 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
{
const QByteArray &responseBody = decryptedResponseBody;
int apiHttpStatus = -1;
QString apiErrorMessage;
int httpStatus = -1;
if (isDecryptionSuccessful) {
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
if (jsonDoc.isObject()) {
QJsonObject jsonObj = jsonDoc.object();
apiHttpStatus = jsonObj.value("http_status").toInt(-1);
apiErrorMessage = jsonObj.value(QStringLiteral("message")).toString().trimmed();
httpStatus = jsonObj.value("http_status").toInt(-1);
}
} else {
qDebug() << "failed to decrypt the data";
@@ -454,12 +428,10 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
qDebug() << "timeout occurred";
qDebug() << replyError;
return true;
}
if (responseBody.contains("html")) {
} else if (responseBody.contains("html")) {
qDebug() << "the response contains an html tag";
return true;
}
if (apiHttpStatus == httpStatusCodeNotFound) {
} else if (httpStatus == httpStatusCodeNotFound) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
@@ -467,25 +439,16 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
qDebug() << replyError;
return true;
}
}
if (apiHttpStatus == httpStatusCodeNotImplemented) {
} else if (httpStatus == httpStatusCodeNotImplemented) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << replyError;
return true;
}
}
if (apiHttpStatus == httpStatusCodeConflict) {
} else if (httpStatus == httpStatusCodeConflict) {
return false;
}
if (apiHttpStatus == httpStatusCodePaymentRequired) {
return false;
}
if (apiHttpStatus == httpStatusCodeUnprocessableEntity) {
return apiErrorMessage != unprocessableSubscriptionMessage;
}
if (replyError != QNetworkReply::NetworkError::NoError) {
} else if (replyError != QNetworkReply::NetworkError::NoError) {
qDebug() << replyError;
return true;
}
@@ -574,7 +537,7 @@ void GatewayController::getProxyUrlsAsync(const QStringList proxyStorageUrls, co
}
QNetworkRequest request;
request.setTransferTimeout(proxyStorageRequestTimeoutMsecs);
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(proxyStorageUrls[currentProxyStorageIndex]);

View File

@@ -8,9 +8,7 @@
#include <QPromise>
#include <QSharedPointer>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/defs.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"

View File

@@ -1,245 +0,0 @@
#include "ipSplitTunnelingController.h"
#include "core/utils/networkUtilities.h"
#include <QJsonObject>
IpSplitTunnelingController::IpSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository, QObject* parent)
: QObject(parent),
m_appSettingsRepository(appSettingsRepository)
{
m_currentRouteMode = m_appSettingsRepository->routeMode();
if (m_currentRouteMode == RouteMode::VpnAllSites) { // for old split tunneling configs
m_appSettingsRepository->setRouteMode(RouteMode::VpnOnlyForwardSites);
m_currentRouteMode = RouteMode::VpnOnlyForwardSites;
}
fillSites();
}
bool IpSplitTunnelingController::addSiteInternal(const QString &hostname, const QString &ip)
{
QVariantMap existing = m_appSettingsRepository->vpnSites(m_currentRouteMode);
if (existing.contains(hostname) && ip.isEmpty()) {
return false;
}
for (int i = 0; i < m_sites.size(); i++) {
if (m_sites[i].first == hostname && (m_sites[i].second.isEmpty() && !ip.isEmpty())) {
m_sites[i].second = ip;
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
return true;
} else if (m_sites[i].first == hostname && (m_sites[i].second == ip)) {
return false;
}
}
m_sites.append(qMakePair(hostname, ip));
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
return true;
}
void IpSplitTunnelingController::addSites(const QMap<QString, QString> &sites, bool replaceExisting)
{
if (replaceExisting) {
m_sites.clear();
}
for (auto it = sites.constBegin(); it != sites.constEnd(); ++it) {
const QString &hostname = it.key();
const QString &ip = it.value();
bool found = false;
for (int i = 0; i < m_sites.size(); i++) {
if (m_sites[i].first == hostname) {
if (!ip.isEmpty()) {
m_sites[i].second = ip;
}
found = true;
break;
}
}
if (!found) {
m_sites.append(qMakePair(hostname, ip));
}
}
if (replaceExisting) {
m_appSettingsRepository->removeAllVpnSites(m_currentRouteMode);
}
m_appSettingsRepository->addVpnSites(m_currentRouteMode, sites);
}
bool IpSplitTunnelingController::addSite(const QString &hostname)
{
QString normalizedHostname = normalizeHostname(hostname);
if (!validateHostname(normalizedHostname)) {
return false;
}
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(normalizedHostname)) {
processSite(normalizedHostname, "");
return true;
}
if (addSiteInternal(normalizedHostname, "")) {
QHostInfo::lookupHost(normalizedHostname, this, SLOT(onHostResolved(QHostInfo)));
return true;
}
return false;
}
bool IpSplitTunnelingController::removeSite(const QString &hostname)
{
for (int i = 0; i < m_sites.size(); i++) {
if (m_sites[i].first == hostname) {
m_sites.removeAt(i);
m_appSettingsRepository->removeVpnSite(m_currentRouteMode, hostname);
return true;
}
}
return false;
}
void IpSplitTunnelingController::removeSites()
{
m_sites.clear();
m_appSettingsRepository->removeAllVpnSites(m_currentRouteMode);
}
void IpSplitTunnelingController::setRouteMode(RouteMode routeMode)
{
m_currentRouteMode = routeMode;
fillSites();
m_appSettingsRepository->setRouteMode(routeMode);
}
void IpSplitTunnelingController::toggleSplitTunneling(bool enabled)
{
m_appSettingsRepository->setSitesSplitTunnelingEnabled(enabled);
}
RouteMode IpSplitTunnelingController::getRouteMode() const
{
return m_currentRouteMode;
}
bool IpSplitTunnelingController::isSplitTunnelingEnabled() const
{
return m_appSettingsRepository->isSitesSplitTunnelingEnabled();
}
QVector<QPair<QString, QString>> IpSplitTunnelingController::getCurrentSites() const
{
return m_sites;
}
void IpSplitTunnelingController::fillSites()
{
QVariantMap sitesMap = m_appSettingsRepository->vpnSites(m_currentRouteMode);
m_sites.clear();
for (auto it = sitesMap.begin(); it != sitesMap.end(); ++it) {
m_sites.append(qMakePair(it.key(), it.value().toString()));
}
}
QString IpSplitTunnelingController::normalizeHostname(const QString &hostname) const
{
QString normalized = hostname;
normalized.replace("https://", "");
normalized.replace("http://", "");
normalized.replace("ftp://", "");
normalized = normalized.split("/", Qt::SkipEmptyParts).first();
return normalized;
}
bool IpSplitTunnelingController::validateHostname(const QString &hostname) const
{
if (hostname.isEmpty()) {
return false;
}
if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
return false;
}
return true;
}
void IpSplitTunnelingController::onHostResolved(const QHostInfo &hostInfo)
{
const QList<QHostAddress> &addresses = hostInfo.addresses();
QString hostname = hostInfo.hostName();
for (const QHostAddress &addr : addresses) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
processSiteAfterResolve(hostname, addr.toString());
break;
}
}
}
void IpSplitTunnelingController::processSiteAfterResolve(const QString &hostname, const QString &ip)
{
for (int i = 0; i < m_sites.size(); i++) {
if (m_sites[i].first == hostname && m_sites[i].second.isEmpty()) {
m_sites[i].second = ip;
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
break;
}
}
}
void IpSplitTunnelingController::processSite(const QString &hostname, const QString &ip)
{
addSiteInternal(hostname, ip);
}
bool IpSplitTunnelingController::importSitesFromJson(const QByteArray& jsonData, bool replaceExisting, QString &errorMessage)
{
QJsonParseError parseError;
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData, &parseError);
if (parseError.error != QJsonParseError::NoError) {
errorMessage = tr("Failed to parse JSON data: %1").arg(parseError.errorString());
return false;
}
if (!jsonDocument.isArray()) {
errorMessage = tr("The JSON data is not an array");
return false;
}
QJsonArray jsonArray = jsonDocument.array();
QMap<QString, QString> sites;
for (auto jsonValue : jsonArray) {
QJsonObject jsonObject = jsonValue.toObject();
QString hostname = jsonObject.value("hostname").toString("");
QString ip = jsonObject.value("ip").toString("");
QString normalizedHostname = normalizeHostname(hostname);
if (!validateHostname(normalizedHostname)) {
qDebug() << normalizedHostname << " not look like ip adress or domain name";
continue;
}
sites.insert(normalizedHostname, ip);
}
addSites(sites, replaceExisting);
return true;
}
QByteArray IpSplitTunnelingController::exportSitesToJson() const
{
QVector<QPair<QString, QString>> sites = getCurrentSites();
QJsonArray jsonArray;
for (const auto &site : sites) {
QJsonObject jsonObject;
jsonObject["hostname"] = site.first;
jsonObject["ip"] = site.second;
jsonArray.append(jsonObject);
}
QJsonDocument jsonDocument(jsonArray);
return jsonDocument.toJson();
}

View File

@@ -1,58 +0,0 @@
#ifndef IPSPLITTUNNELINGCONTROLLER_H
#define IPSPLITTUNNELINGCONTROLLER_H
#include <QObject>
#include <QVector>
#include <QMap>
#include <QPair>
#include <QStringList>
#include <QJsonDocument>
#include <QJsonArray>
#include <QHostInfo>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h"
using namespace amnezia;
class IpSplitTunnelingController : public QObject
{
Q_OBJECT
public:
explicit IpSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository, QObject* parent = nullptr);
bool addSite(const QString &hostname);
void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
bool removeSite(const QString &hostname);
void removeSites();
void setRouteMode(RouteMode routeMode);
void toggleSplitTunneling(bool enabled);
RouteMode getRouteMode() const;
bool isSplitTunnelingEnabled() const;
QVector<QPair<QString, QString>> getCurrentSites() const;
bool importSitesFromJson(const QByteArray& jsonData, bool replaceExisting, QString &errorMessage);
QByteArray exportSitesToJson() const;
private slots:
void onHostResolved(const QHostInfo &hostInfo);
private:
void fillSites();
bool addSiteInternal(const QString &hostname, const QString &ip);
QString normalizeHostname(const QString &hostname) const;
bool validateHostname(const QString &hostname) const;
void processSiteAfterResolve(const QString &hostname, const QString &ip);
void processSite(const QString &hostname, const QString &ip);
SecureAppSettingsRepository* m_appSettingsRepository;
RouteMode m_currentRouteMode;
QVector<QPair<QString, QString>> m_sites;
};
#endif // IPSPLITTUNNELINGCONTROLLER_H

View File

@@ -1,337 +0,0 @@
#include "exportController.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "core/configurators/configuratorBase.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/qrCodeUtils.h"
#include "core/utils/serialization/serialization.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h"
using namespace amnezia;
ExportController::ExportController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent)
: QObject(parent),
m_serversRepository(serversRepository),
m_appSettingsRepository(appSettingsRepository)
{
}
ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex)
{
ExportResult result;
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
serverConfig.visit([](auto& arg) {
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) {
it.value().protocolConfig.clearClientConfig();
}
});
QJsonObject serverJson = serverConfig.toJson();
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig);
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
return result;
}
ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
{
ExportResult result;
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
if (ContainerUtils::containerService(container) != ServiceType::Other) {
SshSession sshSession;
Proto protocol = ContainerUtils::defaultProtocol(container);
DnsSettings dnsSettings = {
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns()
};
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, containerConfig, dnsSettings, result.errorCode);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
containerConfig.protocolConfig = newProtocolConfig;
QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container);
}
}
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
serverConfig.visit([container, containerConfig](auto& arg) {
arg.containers.clear();
arg.containers[container] = containerConfig;
arg.defaultContainer = container;
});
if (serverConfig.isSelfHosted()) {
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>();
if (selfHosted) {
selfHosted->userName.reset();
selfHosted->password.reset();
selfHosted->port.reset();
}
}
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
serverConfig.visit([&dns](auto& arg) {
arg.dns1 = dns.first;
arg.dns2 = dns.second;
});
QJsonObject serverJson = serverConfig.toJson();
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig);
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
return result;
}
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container,
const ContainerConfig &containerConfig,
const QString &clientName)
{
NativeConfigResult result;
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return result;
}
Proto protocol = ContainerUtils::defaultProtocol(container);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
ContainerConfig modifiedContainerConfig = containerConfig;
modifiedContainerConfig.container = container;
DnsSettings dnsSettings = {
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns()
};
SshSession sshSession;
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, modifiedContainerConfig, dnsSettings, result.errorCode);
if (result.errorCode != ErrorCode::NoError) {
return result;
}
ExportSettings exportSettings = { { dns.first, dns.second } };
ProtocolConfig processedConfig = configurator->processConfigWithExportSettings(exportSettings, newProtocolConfig);
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg) {
result.jsonNativeConfig[configKey::config] = processedConfig.nativeConfig();
} else {
result.jsonNativeConfig = QJsonDocument::fromJson(processedConfig.nativeConfig().toUtf8()).object();
}
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container);
}
}
return result;
}
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
{
ExportResult result;
DockerContainer container = DockerContainer::OpenVpn;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode;
return result;
}
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes = generateQrCodesFromConfig(result.config.toUtf8());
return result;
}
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName)
{
ExportResult result;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard);
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode;
return result;
}
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
return result;
}
ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
{
ExportResult result;
DockerContainer container = static_cast<DockerContainer>(containerIndex);
if (container != DockerContainer::Awg && container != DockerContainer::Awg2) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode;
return result;
}
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
return result;
}
ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName)
{
ExportResult result;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray);
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode;
return result;
}
QStringList lines = QString(QJsonDocument(nativeResult.jsonNativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
result.config.append(line + "\n");
}
// Parse the Xray data to extract VLESS parameters and generate string
QJsonObject xrayConfig = nativeResult.jsonNativeConfig;
QJsonArray outbounds = xrayConfig.value(amnezia::protocols::xray::outbounds).toArray();
if (outbounds.isEmpty()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
QJsonObject outbound = outbounds[0].toObject();
QJsonObject settings = outbound.value(amnezia::protocols::xray::settings).toObject();
QJsonObject streamSettings = outbound.value(amnezia::protocols::xray::streamSettings).toObject();
QJsonArray vnext = settings.value(amnezia::protocols::xray::vnext).toArray();
if (vnext.isEmpty()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
QJsonObject server = vnext[0].toObject();
QJsonArray users = server.value(amnezia::protocols::xray::users).toArray();
if (users.isEmpty()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
QJsonObject user = users[0].toObject();
amnezia::serialization::VlessServerObject vlessServer;
vlessServer.address = server.value(amnezia::protocols::xray::address).toString();
vlessServer.port = server.value(amnezia::protocols::xray::port).toInt();
vlessServer.id = user.value(amnezia::protocols::xray::id).toString();
vlessServer.flow = user.value(amnezia::protocols::xray::flow).toString("xtls-rprx-vision");
vlessServer.encryption = user.value(amnezia::protocols::xray::encryption).toString("none");
vlessServer.network = streamSettings.value(amnezia::protocols::xray::network).toString("tcp");
vlessServer.security = streamSettings.value(amnezia::protocols::xray::security).toString("reality");
if (vlessServer.security == "reality") {
QJsonObject realitySettings = streamSettings.value(amnezia::protocols::xray::realitySettings).toObject();
vlessServer.serverName = realitySettings.value(amnezia::protocols::xray::serverName).toString();
vlessServer.publicKey = realitySettings.value(amnezia::protocols::xray::publicKey).toString();
vlessServer.shortId = realitySettings.value(amnezia::protocols::xray::shortId).toString();
vlessServer.fingerprint = realitySettings.value(amnezia::protocols::xray::fingerprint).toString("chrome");
vlessServer.spiderX = realitySettings.value(amnezia::protocols::xray::spiderX).toString("");
}
result.nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
return result;
}
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit updateClientsRequested(serverIndex, container);
}
void ExportController::revokeConfig(int row, int serverIndex, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit revokeClientRequested(serverIndex, row, container);
}
void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit renameClientRequested(serverIndex, row, clientName, container);
}
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
{
return QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
}
QList<QString> ExportController::generateQrCodesFromConfig(const QByteArray &data)
{
return qrCodeUtils::generateQrCodeImageSeries(data);
}
QString ExportController::generateSingleQrCode(const QByteArray &data)
{
auto qr = qrCodeUtils::generateQrCode(data);
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
}

View File

@@ -1,77 +0,0 @@
#ifndef EXPORTCONTROLLER_H
#define EXPORTCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QList>
#include <QString>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
class SshSession;
class VpnConfigurationsController;
using namespace amnezia;
class ExportController : public QObject
{
Q_OBJECT
public:
struct ExportResult
{
ErrorCode errorCode = ErrorCode::NoError;
QString config;
QString nativeConfigString;
QList<QString> qrCodes;
};
explicit ExportController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent = nullptr);
ExportResult generateFullAccessConfig(int serverIndex);
ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName);
ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName);
ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
ExportResult generateXrayConfig(int serverIndex, const QString &clientName);
signals:
void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
void updateClientsRequested(int serverIndex, DockerContainer container);
void revokeClientRequested(int serverIndex, int row, DockerContainer container);
void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container);
public slots:
void updateClientManagementModel(int serverIndex, int containerIndex);
void revokeConfig(int row, int serverIndex, int containerIndex);
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex);
private:
struct NativeConfigResult
{
ErrorCode errorCode = ErrorCode::NoError;
QJsonObject jsonNativeConfig;
};
NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container,
const ContainerConfig &containerConfig,
const QString &clientName);
QString generateVpnUrl(const QByteArray &compressedConfig);
QList<QString> generateQrCodesFromConfig(const QByteArray &data);
QString generateSingleQrCode(const QByteArray &data);
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
};
#endif // EXPORTCONTROLLER_H

View File

@@ -1,762 +0,0 @@
#include "importController.h"
#include <QDataStream>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QMap>
#include <QRandomGenerator>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QRegularExpressionMatchIterator>
#include <QUrl>
#include <algorithm>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/serialization/serialization.h"
#include "core/utils/utilities.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/qrCodeUtils.h"
#include "core/models/serverConfig.h"
using namespace amnezia;
using namespace ProtocolUtils;
namespace
{
ConfigTypes checkConfigFormat(const QString &config)
{
const QString openVpnConfigPatternCli = "client";
const QString openVpnConfigPatternDriver1 = "dev tun";
const QString openVpnConfigPatternDriver2 = "dev tap";
const QString wireguardConfigPatternSectionInterface = "[Interface]";
const QString wireguardConfigPatternSectionPeer = "[Peer]";
const QString xrayConfigPatternInbound = "inbounds";
const QString xrayConfigPatternOutbound = "outbounds";
const QString amneziaConfigPattern = "containers";
const QString amneziaConfigPatternHostName = "hostName";
const QString amneziaConfigPatternUserName = "userName";
const QString amneziaConfigPatternPassword = "password";
const QString amneziaFreeConfigPattern = "api_key";
const QString amneziaPremiumConfigPattern = "auth_data";
const QString backupPattern = "Servers/serversList";
if (config.contains(backupPattern)) {
return ConfigTypes::Backup;
} else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern)
|| config.contains(amneziaPremiumConfigPattern)
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia;
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
return ConfigTypes::WireGuard;
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
return ConfigTypes::Xray;
} else if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn;
}
return ConfigTypes::Invalid;
}
} // namespace
ImportController::ImportController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent)
: QObject(parent),
m_serversRepository(serversRepository),
m_appSettingsRepository(appSettingsRepository)
{
}
ImportController::ImportResult ImportController::extractConfigFromData(const QString &data, const QString &configFileName)
{
ImportResult result;
result.configFileName = configFileName;
result.maliciousWarningText.clear();
QString config = data;
QString prefix;
QString errormsg;
ConfigTypes configType = ConfigTypes::Invalid;
if (config.startsWith("vless://")) {
configType = ConfigTypes::Xray;
result.config = extractXrayConfig(
Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
configType, prefix);
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
if (config.startsWith("vmess://") && config.contains("@")) {
configType = ConfigTypes::Xray;
result.config = extractXrayConfig(
Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
configType, prefix);
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
if (config.startsWith("vmess://")) {
configType = ConfigTypes::Xray;
result.config = extractXrayConfig(
Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
configType, prefix);
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
if (config.startsWith("trojan://")) {
configType = ConfigTypes::Xray;
result.config = extractXrayConfig(
Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
configType, prefix);
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
if (config.startsWith("ss://") && !config.contains("plugin=")) {
configType = ConfigTypes::ShadowSocks;
result.config = extractXrayConfig(
Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
configType, prefix);
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
if (config.startsWith("ssd://")) {
QStringList tmp;
QList<std::pair<QString, QJsonObject>> servers = serialization::ssd::Deserialize(config, &prefix, &tmp);
configType = ConfigTypes::ShadowSocks;
// Took only first config from list
if (!servers.isEmpty()) {
result.config = extractXrayConfig(servers.first().first, configType);
}
if (!result.config.empty()) {
result.configType = configType;
return result;
}
}
configType = checkConfigFormat(config);
if (configType == ConfigTypes::Invalid) {
config.replace("vpn://", "");
QByteArray ba = QByteArray::fromBase64(config.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
QByteArray baUncompressed = qUncompress(ba);
if (!baUncompressed.isEmpty()) {
ba = baUncompressed;
}
config = ba;
configType = checkConfigFormat(config);
}
result.configType = configType;
switch (configType) {
case ConfigTypes::OpenVpn: {
result.config = extractOpenVpnConfig(config);
if (!result.config.empty()) {
checkForMaliciousStrings(result.config, result.maliciousWarningText);
return result;
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
case ConfigTypes::Awg:
case ConfigTypes::WireGuard: {
result.config = extractWireGuardConfig(config, result.configType);
result.isNativeWireGuardConfig = (result.configType == ConfigTypes::WireGuard);
if (!result.config.empty()) {
return result;
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
case ConfigTypes::Xray: {
result.config = extractXrayConfig(config, configType);
if (!result.config.empty()) {
return result;
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
case ConfigTypes::Amnezia: {
result.config = QJsonDocument::fromJson(config.toUtf8()).object();
if (apiUtils::isServerFromApi(result.config)) {
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
apiConfig[apiDefs::key::vpnKey] = data;
result.config[apiDefs::key::apiConfig] = apiConfig;
}
processAmneziaConfig(result.config);
if (!result.config.empty()) {
checkForMaliciousStrings(result.config, result.maliciousWarningText);
return result;
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
case ConfigTypes::Backup: {
result.errorCode = ErrorCode::ImportBackupFileUseRestoreInstead;
return result;
}
case ConfigTypes::Invalid: {
result.errorCode = ErrorCode::ImportInvalidConfigError;
result.configFileName.clear();
return result;
}
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
ImportController::ImportResult ImportController::extractConfigFromQr(const QByteArray &data)
{
ImportResult result;
QString dataStr = QString::fromUtf8(data);
ConfigTypes configType = checkConfigFormat(dataStr);
if (configType != ConfigTypes::Invalid) {
return extractConfigFromData(dataStr, "");
}
QJsonObject dataObj = QJsonDocument::fromJson(data).object();
if (!dataObj.isEmpty()) {
result.config = dataObj;
result.configType = ConfigTypes::Amnezia;
return result;
}
QByteArray ba_uncompressed = qUncompress(data);
if (!ba_uncompressed.isEmpty()) {
result.config = QJsonDocument::fromJson(ba_uncompressed).object();
if (result.config.isEmpty()) {
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
result.configType = ConfigTypes::Amnezia;
return result;
}
QByteArray ba = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
QByteArray baUncompressed = qUncompress(ba);
if (!baUncompressed.isEmpty()) {
ba = baUncompressed;
}
if (!ba.isEmpty()) {
result.config = QJsonDocument::fromJson(ba).object();
if (result.config.isEmpty()) {
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
result.configType = ConfigTypes::Amnezia;
return result;
}
result.errorCode = ErrorCode::ImportInvalidConfigError;
return result;
}
void ImportController::startDecodingQr()
{
m_qrCodeChunks.clear();
m_totalQrCodeChunksCount = 0;
m_receivedQrCodeChunksCount = 0;
m_isQrCodeProcessed = true;
}
ImportController::QrParseResult ImportController::parseQrCodeChunk(const QString &code)
{
QrParseResult parseResult;
parseResult.chunksReceived = m_receivedQrCodeChunksCount;
parseResult.chunksTotal = m_totalQrCodeChunksCount;
if (!m_isQrCodeProcessed) {
return parseResult;
}
QByteArray ba = QByteArray::fromBase64(code.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
QDataStream s(&ba, QIODevice::ReadOnly);
qint16 magic;
s >> magic;
if (magic == qrCodeUtils::qrMagicCode) {
quint8 chunksCount;
s >> chunksCount;
if (m_totalQrCodeChunksCount != chunksCount) {
m_qrCodeChunks.clear();
}
m_totalQrCodeChunksCount = chunksCount;
quint8 chunkId;
s >> chunkId;
s >> m_qrCodeChunks[chunkId];
m_receivedQrCodeChunksCount = m_qrCodeChunks.size();
parseResult.chunksReceived = m_receivedQrCodeChunksCount;
parseResult.chunksTotal = m_totalQrCodeChunksCount;
if (m_qrCodeChunks.size() == m_totalQrCodeChunksCount) {
QByteArray data;
for (int i = 0; i < m_totalQrCodeChunksCount; ++i) {
data.append(m_qrCodeChunks.value(i));
}
ImportResult result = extractConfigFromQr(data);
if (result.errorCode == ErrorCode::NoError) {
parseResult.success = true;
parseResult.importResult = result;
m_isQrCodeProcessed = false;
} else {
m_qrCodeChunks.clear();
m_totalQrCodeChunksCount = 0;
m_receivedQrCodeChunksCount = 0;
}
}
} else {
ImportResult result = extractConfigFromQr(code.toUtf8());
if (result.errorCode != ErrorCode::NoError) {
result = extractConfigFromQr(ba);
}
if (result.errorCode == ErrorCode::NoError) {
parseResult.success = true;
parseResult.importResult = result;
m_isQrCodeProcessed = false;
}
}
return parseResult;
}
bool ImportController::isQrDecodingActive() const
{
return m_isQrCodeProcessed;
}
int ImportController::qrChunksReceived() const
{
return m_receivedQrCodeChunksCount;
}
int ImportController::qrChunksTotal() const
{
return m_totalQrCodeChunksCount;
}
void ImportController::importConfig(const QJsonObject &config)
{
ServerCredentials credentials;
credentials.hostName = config.value(configKey::hostName).toString();
credentials.port = config.value(configKey::port).toInt();
credentials.userName = config.value(configKey::userName).toString();
credentials.secretData = config.value(configKey::password).toString();
if (credentials.isValid() || config.contains(configKey::containers)) {
ServerConfig serverConfig = ServerConfig::fromJson(config);
m_serversRepository->addServer(serverConfig);
emit importFinished();
} else if (config.contains(configKey::configVersion)) {
quint16 crc = qChecksum(QJsonDocument(config).toJson());
if (m_serversRepository->hasServerWithCrc(crc)) {
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
} else {
QJsonObject configWithCrc = config;
configWithCrc.insert(configKey::crc, crc);
ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc);
m_serversRepository->addServer(serverConfig);
emit importFinished();
}
} else {
qDebug() << "Failed to import profile";
qDebug().noquote() << QJsonDocument(config).toJson();
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
}
}
QJsonObject ImportController::processNativeWireGuardConfig(const QJsonObject &config)
{
QJsonObject result = config;
auto containers = result.value(configKey::containers).toArray();
if (!containers.isEmpty()) {
auto container = containers.at(0).toObject();
auto serverProtocolConfig = container.value(ContainerUtils::containerTypeToProtocolString(DockerContainer::WireGuard)).toObject();
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(configKey::lastConfig).toString().toUtf8()).object();
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50);
clientProtocolConfig[configKey::junkPacketCount] = junkPacketCount;
clientProtocolConfig[configKey::junkPacketMinSize] = junkPacketMinSize;
clientProtocolConfig[configKey::junkPacketMaxSize] = junkPacketMaxSize;
clientProtocolConfig[configKey::initPacketJunkSize] = "0";
clientProtocolConfig[configKey::responsePacketJunkSize] = "0";
clientProtocolConfig[configKey::initPacketMagicHeader] = "1";
clientProtocolConfig[configKey::responsePacketMagicHeader] = "2";
clientProtocolConfig[configKey::underloadPacketMagicHeader] = "3";
clientProtocolConfig[configKey::transportPacketMagicHeader] = "4";
clientProtocolConfig[configKey::cookieReplyPacketJunkSize] = "0";
clientProtocolConfig[configKey::transportPacketJunkSize] = "0";
clientProtocolConfig[configKey::specialJunk1] = protocols::awg::defaultSpecialJunk1;
clientProtocolConfig[configKey::isObfuscationEnabled] = true;
serverProtocolConfig[configKey::lastConfig] = QString(QJsonDocument(clientProtocolConfig).toJson());
container[configKey::wireguard] = serverProtocolConfig;
containers.replace(0, container);
result[configKey::containers] = containers;
}
return result;
}
ConfigTypes ImportController::checkConfigFormat(const QString &config) const
{
return ::checkConfigFormat(config);
}
QJsonObject ImportController::extractOpenVpnConfig(const QString &data) const
{
QJsonObject openVpnConfig;
openVpnConfig[configKey::config] = data;
QJsonObject lastConfig;
lastConfig[configKey::lastConfig] = QString(QJsonDocument(openVpnConfig).toJson());
lastConfig[configKey::isThirdPartyConfig] = true;
QJsonObject containers;
containers.insert(configKey::container, QJsonValue(configKey::amneziaOpenvpn));
containers.insert(configKey::openvpn, QJsonValue(lastConfig));
QJsonArray arr;
arr.push_back(containers);
QString hostName;
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
if (hostNameMatch.hasMatch()) {
hostName = hostNameMatch.captured(1);
}
QJsonObject config;
config[configKey::containers] = arr;
config[configKey::defaultContainer] = configKey::amneziaOpenvpn;
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data);
if (dnsMatch.hasNext()) {
config[configKey::dns1] = dnsMatch.next().captured(1);
}
if (dnsMatch.hasNext()) {
config[configKey::dns2] = dnsMatch.next().captured(1);
}
config[configKey::hostName] = hostName;
return config;
}
QJsonObject ImportController::extractWireGuardConfig(const QString &data, ConfigTypes &configType) const
{
QMap<QString, QString> configMap;
auto configByLines = data.split("\n");
for (const QString &line : configByLines) {
QString trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
configMap[parts.at(0).trimmed()] = parts.at(1).trimmed();
}
}
}
QJsonObject lastConfig;
lastConfig[configKey::config] = data;
auto url { QUrl::fromUserInput(configMap.value(protocols::wireguard::Endpoint)) };
QString hostName;
QString port;
if (!url.host().isEmpty()) {
hostName = url.host();
} else {
qDebug() << "Key parameter" << protocols::wireguard::Endpoint << "is missing or has an invalid format";
return QJsonObject();
}
if (url.port() != -1) {
port = QString::number(url.port());
} else {
port = protocols::wireguard::defaultPort;
}
lastConfig[configKey::hostName] = hostName;
lastConfig[configKey::port] = port.toInt();
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 (!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] = configMap.value(protocols::wireguard::PublicKey);
} else {
qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)";
return QJsonObject();
}
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(
configMap.value(protocols::wireguard::AllowedIPs).split(", "));
lastConfig[configKey::allowedIps] = allowedIpsJsonArray;
QString protocolName = configKey::wireguard;
QString protocolVersion;
ConfigTypes detectedType = ConfigTypes::WireGuard;
const QStringList requiredJunkFields = { configKey::junkPacketCount, configKey::junkPacketMinSize,
configKey::junkPacketMaxSize, configKey::initPacketJunkSize,
configKey::responsePacketJunkSize, configKey::initPacketMagicHeader,
configKey::responsePacketMagicHeader, configKey::underloadPacketMagicHeader,
configKey::transportPacketMagicHeader };
const QStringList optionalJunkFields = { configKey::cookieReplyPacketJunkSize,
configKey::transportPacketJunkSize,
configKey::specialJunk1, configKey::specialJunk2, configKey::specialJunk3,
configKey::specialJunk4, configKey::specialJunk5
};
bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(),
[&configMap](const QString &field) { return !configMap.value(field).isEmpty(); });
if (hasAllRequiredFields) {
for (const QString &field : requiredJunkFields) {
lastConfig[field] = configMap.value(field);
}
for (const QString &field : optionalJunkFields) {
if (!configMap.value(field).isEmpty()) {
lastConfig[field] = configMap.value(field);
}
}
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";
} else if (hasSpecialJunk && !hasCookieReplyPacketJunkSize && !hasTransportPacketJunkSize) {
protocolVersion = "1.5";
}
protocolName = configKey::awg;
detectedType = ConfigTypes::Awg;
}
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
: protocols::wireguard::defaultMtu;
}
QJsonObject wireguardConfig;
wireguardConfig[configKey::lastConfig] = QString(QJsonDocument(lastConfig).toJson());
wireguardConfig[configKey::isThirdPartyConfig] = true;
wireguardConfig[configKey::port] = port;
wireguardConfig[configKey::transportProto] = protocols::openvpn::defaultTransportProto;
if (protocolName == configKey::awg && !protocolVersion.isEmpty()) {
wireguardConfig[configKey::protocolVersion] = protocolVersion;
}
QJsonObject containers;
QString containerName = (protocolName == configKey::awg) ? configKey::amneziaAwg : configKey::amneziaWireguard;
containers.insert(configKey::container, QJsonValue(containerName));
containers.insert(protocolName, QJsonValue(wireguardConfig));
QJsonArray arr;
arr.push_back(containers);
QJsonObject config;
config[configKey::containers] = arr;
config[configKey::defaultContainer] = containerName;
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
const static QRegularExpression dnsRegExp(
"DNS = "
"(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
QRegularExpressionMatch dnsMatch = dnsRegExp.match(data);
if (dnsMatch.hasMatch()) {
config[configKey::dns1] = dnsMatch.captured(1);
config[configKey::dns2] = dnsMatch.captured(2);
}
config[configKey::hostName] = hostName;
configType = detectedType;
return config;
}
QJsonObject ImportController::extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description) const
{
QJsonParseError parserErr;
QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr);
QJsonObject xrayVpnConfig;
xrayVpnConfig[configKey::config] = jsonConf.toJson().constData();
QJsonObject lastConfig;
lastConfig[configKey::lastConfig] = jsonConf.toJson().constData();
lastConfig[configKey::isThirdPartyConfig] = true;
QJsonObject containers;
if (configType == ConfigTypes::ShadowSocks) {
containers.insert(configKey::ssxray, QJsonValue(lastConfig));
containers.insert(configKey::container, QJsonValue(configKey::amneziaSsxray));
} else {
containers.insert(configKey::container, QJsonValue(configKey::amneziaXray));
containers.insert(configKey::xray, QJsonValue(lastConfig));
}
QJsonArray arr;
arr.push_back(containers);
QString hostName;
const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)");
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
if (hostNameMatch.hasMatch()) {
hostName = hostNameMatch.captured(1);
}
QJsonObject config;
config[configKey::containers] = arr;
config[configKey::defaultContainer] = (configType == ConfigTypes::ShadowSocks)
? configKey::amneziaSsxray
: configKey::amneziaXray;
if (description.isEmpty()) {
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
} else {
config[configKey::description] = description;
}
config[configKey::hostName] = hostName;
return config;
}
void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig, QString &warningText) const
{
const QJsonArray &containers = serverConfig.value(configKey::containers).toArray();
for (const QJsonValue &container : containers) {
auto containerConfig = container.toObject();
auto containerName = containerConfig[configKey::container].toString();
if (containerName == ContainerUtils::containerToString(DockerContainer::OpenVpn)) {
QString protocolConfig =
containerConfig[ProtocolUtils::protoToString(Proto::OpenVpn)].toObject()[configKey::lastConfig].toString();
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[configKey::config].toString();
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
QStringList dangerousTags {
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
};
QStringList maliciousStrings;
QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts);
for (const QString &rawLine : lines) {
QString line = rawLine.trimmed();
QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty);
if (dangerousTags.contains(command, Qt::CaseInsensitive)) {
maliciousStrings << rawLine;
}
}
warningText = "This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
"scripts, so only add it if you fully trust the provider of this config. ";
if (!maliciousStrings.isEmpty()) {
warningText += "<br>In the imported configuration, potentially dangerous lines were found:";
for (const auto &string : maliciousStrings) {
warningText += QString("<br><i>%1</i>").arg(string);
}
}
}
}
}
void ImportController::processAmneziaConfig(QJsonObject &config) const
{
auto containers = config.value(configKey::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
auto dockerContainer = ContainerUtils::containerFromString(container.value(configKey::container).toString());
if (ContainerUtils::isAwgContainer(dockerContainer) || dockerContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerUtils::containerTypeToProtocolString(dockerContainer)).toObject();
auto protocolConfig = containerConfig.value(configKey::lastConfig).toString();
if (protocolConfig.isEmpty()) {
return;
}
QJsonObject jsonConfig = QJsonDocument::fromJson(protocolConfig.toUtf8()).object();
jsonConfig[configKey::mtu] =
ContainerUtils::isAwgContainer(dockerContainer) ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
containerConfig[configKey::lastConfig] = QString(QJsonDocument(jsonConfig).toJson());
container[ContainerUtils::containerTypeToProtocolString(dockerContainer)] = containerConfig;
containers.replace(i, container);
config.insert(configKey::containers, containers);
}
}
}

View File

@@ -1,91 +0,0 @@
#ifndef IMPORTCONTROLLER_H
#define IMPORTCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QByteArray>
#include <QMap>
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
namespace
{
enum class ConfigTypes {
Amnezia,
OpenVpn,
WireGuard,
Awg,
Xray,
ShadowSocks,
Backup,
Invalid
};
}
using namespace amnezia;
class ImportController : public QObject
{
Q_OBJECT
public:
struct ImportResult
{
ErrorCode errorCode = ErrorCode::NoError;
QJsonObject config;
QString configFileName;
QString maliciousWarningText;
ConfigTypes configType = ConfigTypes::Invalid;
bool isNativeWireGuardConfig = false;
};
explicit ImportController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent = nullptr);
struct QrParseResult {
bool success = false;
ImportResult importResult;
int chunksReceived = 0;
int chunksTotal = 0;
};
ImportResult extractConfigFromData(const QString &data, const QString &configFileName = "");
ImportResult extractConfigFromQr(const QByteArray &data);
void startDecodingQr();
QrParseResult parseQrCodeChunk(const QString &code);
bool isQrDecodingActive() const;
int qrChunksReceived() const;
int qrChunksTotal() const;
void importConfig(const QJsonObject &config);
QJsonObject processNativeWireGuardConfig(const QJsonObject &config);
signals:
void importFinished();
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void restoreAppConfig(const QByteArray &data);
private:
ConfigTypes checkConfigFormat(const QString &config) const;
QJsonObject extractOpenVpnConfig(const QString &data) const;
QJsonObject extractWireGuardConfig(const QString &data, ConfigTypes &configType) const;
QJsonObject extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description = "") const;
void checkForMaliciousStrings(const QJsonObject &serverConfig, QString &warningText) const;
void processAmneziaConfig(QJsonObject &config) const;
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
QMap<int, QByteArray> m_qrCodeChunks;
bool m_isQrCodeProcessed = false;
int m_totalQrCodeChunksCount = 0;
int m_receivedQrCodeChunksCount = 0;
};
#endif // IMPORTCONTROLLER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +0,0 @@
#ifndef INSTALLCONTROLLER_H
#define INSTALLCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QScopedPointer>
#include <QSharedPointer>
#include <QProcess>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/models/containerConfig.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
class SshSession;
class InstallerBase;
using namespace amnezia;
class InstallController : public QObject
{
Q_OBJECT
public:
explicit InstallController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent = nullptr);
~InstallController();
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
ErrorCode rebootServer(int serverIndex);
ErrorCode removeAllContainers(int serverIndex);
ErrorCode removeContainer(int serverIndex, DockerContainer container);
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession);
ErrorCode scanServerForInstalledContainers(int serverIndex);
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled);
ErrorCode installContainer(int serverIndex, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled);
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
ErrorCode checkSshConnection(const ServerCredentials &credentials, QString &output, std::function<QString()> passphraseCallback = nullptr);
bool isServerAlreadyExists(const ServerCredentials &credentials, int &existingServerIndex);
ErrorCode mountSftpDrive(const ServerCredentials &credentials, const QString &port, const QString &password, const QString &username);
void stopAllSftpMounts();
void cancelInstallation();
void clearCachedProfile(int serverIndex, DockerContainer container);
ErrorCode validateAndPrepareConfig(int serverIndex);
void validateConfig(int serverIndex);
signals:
void configValidated(bool isValid);
void validationErrorOccurred(ErrorCode errorCode);
void serverIsBusy(const bool isBusy);
void cancelInstallationRequested();
void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container);
void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession);
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
ErrorCode isUserInSudo(const ServerCredentials &credentials, SshSession &sshSession);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, SshSession &sshSession);
ErrorCode setupServerFirewall(const ServerCredentials &credentials, SshSession &sshSession);
bool isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
ErrorCode prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession);
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
const ServerCredentials &credentials, SshSession &sshSession,
int serverIndex, const QString &clientName);
void adminAppendRequested(int serverIndex, DockerContainer container,
const ContainerConfig &containerConfig, const QString &clientName);
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
QScopedPointer<InstallerBase> createInstaller(DockerContainer container);
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
bool m_cancelInstallation = false;
#ifndef Q_OS_IOS
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
#endif
};
#endif // INSTALLCONTROLLER_H

View File

@@ -1,807 +0,0 @@
#include "usersController.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
#include "logger.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
using namespace amnezia;
namespace
{
Logger logger("UsersController");
}
UsersController::UsersController(SecureServersRepository* serversRepository, QObject *parent)
: QObject(parent),
m_serversRepository(serversRepository)
{
}
bool UsersController::isClientExists(const QString &clientId, const QJsonArray &clientsTable)
{
for (const QJsonValue &value : std::as_const(clientsTable)) {
if (value.isObject()) {
QJsonObject obj = value.toObject();
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
return true;
}
}
}
return false;
}
int UsersController::clientIndexById(const QString &clientId, const QJsonArray &clientsTable)
{
for (int i = 0; i < clientsTable.size(); ++i) {
if (clientsTable.at(i).isObject()) {
QJsonObject obj = clientsTable.at(i).toObject();
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
return i;
}
}
}
return -1;
}
void UsersController::migration(const QByteArray &clientsTableString, QJsonArray &clientsTable)
{
QJsonObject clientsTableObj = QJsonDocument::fromJson(clientsTableString).object();
for (auto &clientId : clientsTableObj.keys()) {
QJsonObject client;
client[configKey::clientId] = clientId;
QJsonObject userData;
userData[configKey::clientName] = clientsTableObj.value(clientId).toObject().value(configKey::clientName);
client[configKey::userData] = userData;
clientsTable.push_back(client);
}
}
ErrorCode UsersController::wgShow(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, std::vector<WgShowData> &data)
{
if (container != DockerContainer::WireGuard && !ContainerUtils::isAwgContainer(container)) {
return ErrorCode::NoError;
}
ErrorCode error = ErrorCode::NoError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
QString showBin = (container == DockerContainer::Awg2)
? QStringLiteral("awg")
: QStringLiteral("wg");
const QString command = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%1 show all'").arg(showBin);
QString script = sshSession->replaceVars(command, amnezia::genBaseVars(credentials, container, QString(), QString()));
error = sshSession->runScript(credentials, script, cbReadStdOut);
if (error != ErrorCode::NoError) {
logger.error() << QString("Failed to execute %1 show command").arg(showBin);
return error;
}
if (stdOut.isEmpty()) {
return error;
}
const auto getStrValue = [](const auto str) { return str.mid(str.indexOf(":") + 1).trimmed(); };
const auto parts = stdOut.split('\n');
const auto peerList = parts.filter("peer:");
const auto latestHandshakeList = parts.filter("latest handshake:");
const auto transferredDataList = parts.filter("transfer:");
const auto allowedIpsList = parts.filter("allowed ips:");
if (allowedIpsList.isEmpty() || latestHandshakeList.isEmpty() || transferredDataList.isEmpty() || peerList.isEmpty()) {
return error;
}
const auto changeHandshakeFormat = [](QString &latestHandshake) {
const std::vector<std::pair<QString, QString>> replaceMap = { { " days", "d" }, { " hours", "h" }, { " minutes", "m" },
{ " seconds", "s" }, { " day", "d" }, { " hour", "h" },
{ " minute", "m" }, { " second", "s" } };
for (const auto &item : replaceMap) {
latestHandshake.replace(item.first, item.second);
}
};
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size() && i < allowedIpsList.size(); ++i) {
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
auto latestHandshake = getStrValue(latestHandshakeList[i]);
auto serverBytesReceived = transferredData.front().trimmed();
auto serverBytesSent = transferredData.back().trimmed();
auto allowedIps = getStrValue(allowedIpsList[i]);
changeHandshakeFormat(latestHandshake);
serverBytesReceived.chop(QStringLiteral(" received").length());
serverBytesSent.chop(QStringLiteral(" sent").length());
data.push_back({ getStrValue(peerList[i]), latestHandshake, serverBytesSent, serverBytesReceived, allowedIps });
}
return error;
}
ErrorCode UsersController::getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable)
{
ErrorCode error = ErrorCode::NoError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
const QString getOpenVpnClientsList = "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'";
QString script = sshSession->replaceVars(getOpenVpnClientsList, amnezia::genBaseVars(credentials, container, QString(), QString()));
error = sshSession->runScript(credentials, script, cbReadStdOut);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to retrieve the list of issued certificates on the server";
return error;
}
if (!stdOut.isEmpty()) {
QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts);
certsIds.removeAll("AmneziaReq.crt");
for (auto &openvpnCertId : certsIds) {
openvpnCertId.replace(".crt", "");
if (!isClientExists(openvpnCertId, clientsTable)) {
QJsonObject client;
client[configKey::clientId] = openvpnCertId;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
clientsTable.push_back(client);
count++;
}
}
}
return error;
}
ErrorCode UsersController::getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable)
{
ErrorCode error = ErrorCode::NoError;
QString configPath;
if (container == DockerContainer::Awg) {
configPath = QString::fromLatin1(protocols::awg::serverLegacyConfigPath);
} else if (container == DockerContainer::Awg2) {
configPath = QString::fromLatin1(protocols::awg::serverConfigPath);
} else {
configPath = QString::fromLatin1(protocols::wireguard::serverConfigPath);
}
const QString wireguardConfigString = sshSession->getTextFileFromContainer(container, credentials, configPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the wg conf file from the server";
return error;
}
auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts);
QStringList wireguardKeys;
for (const auto &line : configLines) {
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
if (configPair.front() == "PublicKey") {
wireguardKeys.push_back(configPair.back());
}
}
for (auto &wireguardKey : wireguardKeys) {
if (!isClientExists(wireguardKey, clientsTable)) {
QJsonObject client;
client[configKey::clientId] = wireguardKey;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
clientsTable.push_back(client);
count++;
}
}
return error;
}
ErrorCode UsersController::getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable)
{
ErrorCode error = ErrorCode::NoError;
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = sshSession->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the xray server config file from the server";
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
logger.error() << "Failed to parse xray server config JSON";
return ErrorCode::InternalError;
}
if (!serverConfig.object().contains(protocols::xray::inbounds) || serverConfig.object()[protocols::xray::inbounds].toArray().isEmpty()) {
logger.error() << "Invalid xray server config structure";
return ErrorCode::InternalError;
}
const QJsonObject inbound = serverConfig.object()[protocols::xray::inbounds].toArray()[0].toObject();
if (!inbound.contains(protocols::xray::settings)) {
logger.error() << "Missing settings in xray inbound config";
return ErrorCode::InternalError;
}
const QJsonObject settings = inbound[protocols::xray::settings].toObject();
if (!settings.contains(protocols::xray::clients)) {
logger.error() << "Missing clients in xray settings config";
return ErrorCode::InternalError;
}
const QJsonArray clients = settings[protocols::xray::clients].toArray();
for (const auto &clientValue : clients) {
const QJsonObject clientObj = clientValue.toObject();
if (!clientObj.contains(protocols::xray::id)) {
logger.error() << "Missing id in xray client config";
continue;
}
QString clientId = clientObj[protocols::xray::id].toString();
QString xrayDefaultUuid = sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
xrayDefaultUuid.replace("\n", "");
if (!isClientExists(clientId, clientsTable) && clientId != xrayDefaultUuid) {
QJsonObject client;
client[configKey::clientId] = clientId;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
clientsTable.push_back(client);
count++;
}
}
return error;
}
ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container)
{
ErrorCode error = ErrorCode::NoError;
SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn) {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
}
const QByteArray clientsTableString = sshSession.getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server";
emit clientsUpdated(QJsonArray());
return error;
}
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) {
migration(clientsTableString, m_clientsTable);
int count = 0;
if (container == DockerContainer::OpenVpn) {
error = getOpenVpnClients(container, credentials, &sshSession, count, m_clientsTable);
} else if (container == DockerContainer::WireGuard || ContainerUtils::isAwgContainer(container)) {
error = getWireGuardClients(container, credentials, &sshSession, count, m_clientsTable);
} else if (container == DockerContainer::Xray) {
error = getXrayClients(container, credentials, &sshSession, count, m_clientsTable);
}
if (error != ErrorCode::NoError) {
emit clientsUpdated(QJsonArray());
return error;
}
const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson();
if (clientsTableString != newClientsTableString) {
error = sshSession.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
}
}
}
std::vector<WgShowData> data;
wgShow(container, credentials, &sshSession, data);
for (const auto &client : data) {
int i = 0;
for (const auto &it : std::as_const(m_clientsTable)) {
if (it.isObject()) {
QJsonObject obj = it.toObject();
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == client.clientId) {
QJsonObject userData = obj[configKey::userData].toObject();
if (!client.latestHandshake.isEmpty()) {
userData[configKey::latestHandshake] = client.latestHandshake;
}
if (!client.dataReceived.isEmpty()) {
userData[configKey::dataReceived] = client.dataReceived;
}
if (!client.dataSent.isEmpty()) {
userData[configKey::dataSent] = client.dataSent;
}
if (!client.allowedIps.isEmpty()) {
userData[configKey::allowedIps] = client.allowedIps;
}
obj[configKey::userData] = userData;
m_clientsTable.replace(i, obj);
break;
}
}
++i;
}
}
emit clientsUpdated(m_clientsTable);
return error;
}
ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container)
{
ErrorCode error = ErrorCode::NoError;
SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
error = updateClients(serverIndex, container);
if (error != ErrorCode::NoError) {
return error;
}
int existingIndex = clientIndexById(clientId, m_clientsTable);
if (existingIndex >= 0) {
return renameClient(serverIndex, existingIndex, clientName, container, true);
}
QJsonObject client;
client[configKey::clientId] = clientId;
QJsonObject userData;
userData[configKey::clientName] = clientName;
userData[configKey::creationDate] = QDateTime::currentDateTime().toString();
client[configKey::userData] = userData;
m_clientsTable.push_back(client);
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn) {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
}
error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
emit clientAdded(client);
emit clientsUpdated(m_clientsTable);
return error;
}
ErrorCode UsersController::renameClient(int serverIndex, const int row, const QString &clientName,
const DockerContainer container, bool addTimeStamp)
{
if (row < 0 || row >= m_clientsTable.size()) {
return ErrorCode::InternalError;
}
SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
auto client = m_clientsTable.at(row).toObject();
auto userData = client[configKey::userData].toObject();
userData[configKey::clientName] = clientName;
if (addTimeStamp) {
userData[configKey::creationDate] = QDateTime::currentDateTime().toString();
}
client[configKey::userData] = userData;
m_clientsTable.replace(row, client);
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn) {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
}
ErrorCode error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
if (addTimeStamp) {
emit clientsUpdated(m_clientsTable);
} else {
emit clientRenamed(row, clientName);
}
return error;
}
ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable)
{
if (row < 0 || row >= clientsTable.size()) {
return ErrorCode::InternalError;
}
auto client = clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '"
"cd /opt/amnezia/openvpn ;\\"
"easyrsa revoke %1 ;\\"
"easyrsa gen-crl ;\\"
"chmod 666 pki/crl.pem ;\\"
"cp pki/crl.pem .'")
.arg(clientId);
const QString script = sshSession->replaceVars(getOpenVpnCertData, amnezia::genBaseVars(credentials, container, QString(), QString()));
ErrorCode error = sshSession->runScript(credentials, script);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to revoke the certificate";
return error;
}
clientsTable.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
return ErrorCode::NoError;
}
ErrorCode UsersController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable)
{
if (row < 0 || row >= clientsTable.size()) {
return ErrorCode::InternalError;
}
ErrorCode error = ErrorCode::NoError;
QString configPath;
if (container == DockerContainer::Awg) {
configPath = QString::fromLatin1(protocols::awg::serverLegacyConfigPath);
} else if (container == DockerContainer::Awg2) {
configPath = QString::fromLatin1(protocols::awg::serverConfigPath);
} else {
configPath = QString::fromLatin1(protocols::wireguard::serverConfigPath);
}
const QString wireguardConfigString = sshSession->getTextFileFromContainer(container, credentials, configPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the wg conf file from the server";
return error;
}
auto client = clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts);
for (auto &section : configSections) {
if (section.contains(clientId)) {
configSections.removeOne(section);
break;
}
}
QString newWireGuardConfig = configSections.join("[");
newWireGuardConfig.insert(0, "[");
error = sshSession->uploadTextFileToContainer(container, credentials, newWireGuardConfig, configPath);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the wg conf file to the server";
return error;
}
clientsTable.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn) {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
}
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
return error;
}
bool isAwg2 = (container == DockerContainer::Awg2);
QString command = isAwg2 ? QStringLiteral("awg") : QStringLiteral("wg");
QString iface = isAwg2 ? QStringLiteral("awg0") : QStringLiteral("wg0");
QString script = QString(
"sudo docker exec -i $CONTAINER_NAME bash -c '%1 syncconf %2 <(%1-quick strip %3)'"
).arg(command, iface, configPath);
error = sshSession->runScript(
credentials,
sshSession->replaceVars(script, amnezia::genBaseVars(credentials, container, QString(), QString()))
);
if (error != ErrorCode::NoError) {
logger.error() << QString("Failed to execute command '%1 syncconf %2' on the server").arg(command, iface);
return error;
}
return ErrorCode::NoError;
}
ErrorCode UsersController::revokeXray(const int row,
const DockerContainer container,
const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable)
{
if (row < 0 || row >= clientsTable.size()) {
return ErrorCode::InternalError;
}
ErrorCode error = ErrorCode::NoError;
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = sshSession->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the xray server config file";
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
logger.error() << "Failed to parse xray server config JSON";
return ErrorCode::InternalError;
}
auto client = clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
QJsonObject configObj = serverConfig.object();
if (!configObj.contains(protocols::xray::inbounds)) {
logger.error() << "Missing inbounds in xray config";
return ErrorCode::InternalError;
}
QJsonArray inbounds = configObj[protocols::xray::inbounds].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Empty inbounds array in xray config";
return ErrorCode::InternalError;
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains(protocols::xray::settings)) {
logger.error() << "Missing settings in xray inbound config";
return ErrorCode::InternalError;
}
QJsonObject settings = inbound[protocols::xray::settings].toObject();
if (!settings.contains(protocols::xray::clients)) {
logger.error() << "Missing clients in xray settings";
return ErrorCode::InternalError;
}
QJsonArray clients = settings[protocols::xray::clients].toArray();
if (clients.isEmpty()) {
logger.error() << "Empty clients array in xray config";
return ErrorCode::InternalError;
}
for (int i = 0; i < clients.size(); ++i) {
QJsonObject clientObj = clients[i].toObject();
if (clientObj.contains(protocols::xray::id) && clientObj[protocols::xray::id].toString() == clientId) {
clients.removeAt(i);
break;
}
}
settings[protocols::xray::clients] = clients;
inbound[protocols::xray::settings] = settings;
inbounds[0] = inbound;
configObj[protocols::xray::inbounds] = inbounds;
error = sshSession->uploadTextFileToContainer(
container,
credentials,
QJsonDocument(configObj).toJson(),
serverConfigPath
);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload updated xray config";
return error;
}
clientsTable.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable")
.arg(ContainerUtils::containerTypeToString(container));
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file";
}
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
error = sshSession->runScript(
credentials,
sshSession->replaceVars(restartScript, amnezia::genBaseVars(credentials, container, QString(), QString()))
);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to restart xray container";
return error;
}
return error;
}
ErrorCode UsersController::revokeClient(int serverIndex, const int index, const DockerContainer container)
{
if (index < 0 || index >= m_clientsTable.size()) {
return ErrorCode::InternalError;
}
SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString();
ErrorCode errorCode = ErrorCode::NoError;
switch(container)
{
case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable);
break;
}
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Awg2: {
errorCode = revokeWireGuard(index, container, credentials, &sshSession, m_clientsTable);
break;
}
case DockerContainer::Xray: {
errorCode = revokeXray(index, container, credentials, &sshSession, m_clientsTable);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container);
QString containerClientId = containerCfg.protocolConfig.clientId();
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
emit adminConfigRevoked(serverIndex, container);
}
emit clientRevoked(index);
emit clientsUpdated(m_clientsTable);
}
return errorCode;
}
ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container)
{
SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ErrorCode errorCode = ErrorCode::NoError;
errorCode = updateClients(serverIndex, container);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
Proto protocol = containerConfig.getProtocolType();
switch(container)
{
case DockerContainer::OpenVpn:
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Awg2:
case DockerContainer::Xray: {
protocol = ContainerUtils::defaultProtocol(container);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
QString clientId = containerConfig.protocolConfig.clientId();
int row = clientIndexById(clientId, m_clientsTable);
if (row < 0) {
return errorCode;
}
switch (container)
{
case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable);
break;
}
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Awg2: {
errorCode = revokeWireGuard(row, container, credentials, &sshSession, m_clientsTable);
break;
}
case DockerContainer::Xray: {
errorCode = revokeXray(row, container, credentials, &sshSession, m_clientsTable);
break;
}
default:
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
if (errorCode == ErrorCode::NoError) {
emit adminConfigRevoked(serverIndex, container);
emit clientRevoked(row);
emit clientsUpdated(m_clientsTable);
}
return errorCode;
}

View File

@@ -1,76 +0,0 @@
#ifndef USERSCONTROLLER_H
#define USERSCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h"
class UsersController : public QObject
{
Q_OBJECT
public:
struct WgShowData
{
QString clientId;
QString latestHandshake;
QString dataReceived;
QString dataSent;
QString allowedIps;
};
explicit UsersController(SecureServersRepository* serversRepository, QObject *parent = nullptr);
signals:
void clientsUpdated(const QJsonArray &clients);
void clientAdded(const QJsonObject &client);
void clientRenamed(int row, const QString &newName);
void clientRevoked(int row);
void adminConfigRevoked(int serverIndex, DockerContainer container);
public slots:
ErrorCode updateClients(int serverIndex, const DockerContainer container);
ErrorCode appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container);
ErrorCode renameClient(int serverIndex, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
ErrorCode revokeClient(int serverIndex, const int index, const DockerContainer container);
ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container);
private:
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
int clientIndexById(const QString &clientId, const QJsonArray &clientsTable);
void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable);
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex,
SshSession* sshSession, QJsonArray &clientsTable);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable);
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable);
ErrorCode getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable);
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable);
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable);
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, std::vector<WgShowData> &data);
SecureServersRepository* m_serversRepository;
QJsonArray m_clientsTable;
};
#endif // USERSCONTROLLER_H

View File

@@ -0,0 +1,887 @@
#include "serverController.h"
#include <QCryptographicHash>
#include <QDir>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include <QPointer>
#include <QTemporaryFile>
#include <QThread>
#include <QTimer>
#include <QtConcurrent>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sys/stat.h>
#include <chrono>
#include <thread>
#include "containers/containers_defs.h"
#include "core/networkUtilities.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "logger.h"
#include "settings.h"
#include "utilities.h"
#include "vpnConfigurationController.h"
namespace
{
Logger logger("ServerController");
}
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
{
}
ServerController::~ServerController()
{
m_sshClient.disconnectFromHost();
}
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
{
auto error = m_sshClient.connectToHost(credentials);
if (error != ErrorCode::NoError) {
return error;
}
script.replace("\r", "");
qDebug() << "ServerController::Run script";
QString totalLine;
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
for (int i = 0; i < lines.count(); i++) {
QString currentLine = lines.at(i);
if (totalLine.isEmpty()) {
totalLine = currentLine;
} else {
totalLine = totalLine + "\n" + currentLine;
}
QString lineToExec;
if (currentLine.endsWith("\\")) {
continue;
} else {
lineToExec = totalLine;
totalLine.clear();
}
if (lineToExec.startsWith("#")) {
continue;
}
qDebug().noquote() << lineToExec;
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) {
return error;
}
}
qDebug().noquote() << "ServerController::runScript finished\n";
return ErrorCode::NoError;
}
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
{
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e)
return e;
QString runner =
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
return e;
}
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path, libssh::ScpOverwriteMode overwriteMode)
{
ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
if (e)
return e;
QString stdOut;
auto cbReadStd = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
// mkdir
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
if (e)
return e;
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
return e;
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
return e;
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
return e;
} else
return ErrorCode::NotImplementedError;
if (stdOut.contains("Error") && stdOut.contains("No such container")) {
return ErrorCode::ServerContainerMissingError;
}
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e;
}
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
ErrorCode &errorCode)
{
errorCode = ErrorCode::NoError;
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
errorCode = runScript(credentials, script, cbReadStdOut);
return QByteArray::fromHex(stdOut.toUtf8());
}
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
libssh::ScpOverwriteMode overwriteMode)
{
auto error = m_sshClient.connectToHost(credentials);
if (error != ErrorCode::NoError) {
return error;
}
QTemporaryFile localFile;
localFile.open();
localFile.write(data);
localFile.close();
error = m_sshClient.scpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
if (error != ErrorCode::NoError) {
return error;
}
return ErrorCode::NoError;
}
ErrorCode ServerController::rebootServer(const ServerCredentials &credentials)
{
QString script = QString("sudo reboot");
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
return runScript(credentials, script, cbReadStdOut, cbReadStdErr);
}
ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials)
{
return runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
}
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
{
return runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
{
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
ErrorCode e = ErrorCode::NoError;
e = isUserInSudo(credentials, container);
if (e)
return e;
e = isServerDpkgBusy(credentials, container);
if (e)
return e;
e = installDockerWorker(credentials, container);
if (e)
return e;
qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished";
if (!isUpdate) {
e = isServerPortBusy(credentials, container, config);
if (e)
return e;
}
if (!isUpdate) {
e = isServerPortBusy(credentials, container, config);
if (e)
return e;
}
e = prepareHostWorker(credentials, container, config);
if (e)
return e;
qDebug().noquote() << "ServerController::setupContainer prepareHostWorker finished";
removeContainer(credentials, container);
qDebug().noquote() << "ServerController::setupContainer removeContainer finished";
qDebug().noquote() << "buildContainerWorker start";
e = buildContainerWorker(credentials, container, config);
if (e)
return e;
qDebug().noquote() << "ServerController::setupContainer buildContainerWorker finished";
e = runContainerWorker(credentials, container, config);
if (e)
return e;
qDebug().noquote() << "ServerController::setupContainer runContainerWorker finished";
e = configureContainerWorker(credentials, container, config);
if (e)
return e;
qDebug().noquote() << "ServerController::setupContainer configureContainerWorker finished";
setupServerFirewall(credentials);
qDebug().noquote() << "ServerController::setupContainer setupServerFirewall finished";
return startupContainerWorker(credentials, container, config);
}
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
QJsonObject &newConfig)
{
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
if (reinstallRequired) {
return setupContainer(credentials, container, newConfig, true);
} else {
ErrorCode e = configureContainerWorker(credentials, container, newConfig);
if (e)
return e;
return startupContainerWorker(credentials, container, newConfig);
}
}
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
{
Proto mainProto = ContainerProps::defaultProtocol(container);
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
if (container == DockerContainer::OpenVpn) {
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto)
!= newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
return true;
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
return true;
}
if (container == DockerContainer::Cloak) {
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
return true;
}
if (container == DockerContainer::ShadowSocks) {
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
return true;
}
if (ContainerProps::isAwgContainer(container)) {
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
!= newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize))
|| (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)
!= newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize))
|| (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)
!= newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize))
|| (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)
!= newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize))
|| (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)
!= newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader))
|| (oldProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|| (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
!= newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
|| (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
!= newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)))
return true;
}
if (container == DockerContainer::WireGuard) {
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
return true;
}
if (container == DockerContainer::Socks5Proxy) {
return true;
}
if (container == DockerContainer::Xray) {
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
return true;
}
}
return false;
}
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &client) {
stdOut += data + "\n";
if (data.contains("Automatically restart Docker daemon?")) {
return client.writeResponse("yes");
}
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
ErrorCode error =
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
if (container == DockerContainer::Awg2) {
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
QRegularExpressionMatch match = regex.match(stdOut);
if (match.hasMatch()) {
int majorVersion = match.captured(1).toInt();
int minorVersion = match.captured(2).toInt();
if (majorVersion < 4 || (majorVersion == 4 && minorVersion < 14)) {
return ErrorCode::ServerLinuxKernelTooOld;
}
}
}
if (stdOut.contains("lock"))
return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("command not found"))
return ErrorCode::ServerDockerFailedError;
return error;
}
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
// create folder on host
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString dockerFilePath = amnezia::server::getDockerfileFolder(container) + "/Dockerfile";
QString scriptString = QString("sudo rm %1").arg(dockerFilePath);
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, genVarsForScript(credentials, container)));
if (errorCode)
return errorCode;
errorCode = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(), dockerFilePath);
if (errorCode)
return errorCode;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
ErrorCode error =
runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("doesn't work on cgroups v2"))
return ErrorCode::ServerDockerOnCgroupsV2;
if (stdOut.contains("cgroup mountpoint does not exist"))
return ErrorCode::ServerCgroupMountpoint;
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
return ErrorCode::DockerPullRateLimit;
return error;
}
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
ErrorCode e = runScript(credentials,
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
genVarsForScript(credentials, container, config)),
cbReadStdOut);
if (stdOut.contains("address already in use"))
return ErrorCode::ServerPortAlreadyAllocatedError;
if (stdOut.contains("is already in use by container"))
return ErrorCode::ServerPortAlreadyAllocatedError;
if (stdOut.contains("invalid publish"))
return ErrorCode::ServerDockerFailedError;
return e;
}
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
ErrorCode e = runContainerScript(credentials, container,
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
genVarsForScript(credentials, container, config)),
cbReadStdOut, cbReadStdErr);
VpnConfigurationsController::updateContainerConfigAfterInstallation(container, config, stdOut);
return e;
}
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
if (script.isEmpty()) {
return ErrorCode::NoError;
}
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, genVarsForScript(credentials, container, config)),
"/opt/amnezia/start.sh");
if (e)
return e;
return runScript(credentials,
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && "
"/opt/amnezia/start.sh\"",
genVarsForScript(credentials, container, config)));
}
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config)
{
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
Vars vars;
vars.append({ { "$REMOTE_HOST", credentials.hostName } });
// OpenVPN vars
vars.append({ { "$OPENVPN_SUBNET_IP",
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
vars.append({ { "$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } });
vars.append({ { "$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } });
vars.append({ { "$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
vars.append({ { "$OPENVPN_TRANSPORT_PROTO",
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } });
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } });
vars.append({ { "$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) } });
vars.append({ { "$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) } });
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
vars.append({ { "$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" } });
if (!isTlsAuth) {
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
vars.append({ { "$OPENVPN_TA_KEY", "" } });
}
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
openvpnConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig) } });
vars.append({ { "$OPENVPN_ADDITIONAL_SERVER_CONFIG",
openvpnConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig) } });
// ShadowSocks vars
vars.append({ { "$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) } });
vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } });
vars.append({ { "$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) } });
vars.append({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
// Cloak vars
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } });
vars.append({ { "$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
// Xray vars
vars.append({ { "$XRAY_SITE_NAME", xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
vars.append({ { "$XRAY_SERVER_PORT", xrayConfig.value(config_key::port).toString(protocols::xray::defaultPort) } });
// Wireguard vars
vars.append({ { "$WIREGUARD_SUBNET_IP",
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
vars.append({ { "$WIREGUARD_SUBNET_CIDR",
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
vars.append({ { "$WIREGUARD_SUBNET_MASK",
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } });
vars.append({ { "$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) } });
// IPsec vars
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
vars.append({ { "$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250" } });
vars.append({ { "$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1" } });
vars.append({ { "$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24" } });
vars.append({ { "$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250" } });
vars.append({ { "$IPSEC_VPN_SHA2_TRUNCBUG", "yes" } });
vars.append({ { "$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes" } });
vars.append({ { "$IPSEC_VPN_DISABLE_IKEV2", "no" } });
vars.append({ { "$IPSEC_VPN_DISABLE_L2TP", "no" } });
vars.append({ { "$IPSEC_VPN_DISABLE_XAUTH", "no" } });
vars.append({ { "$IPSEC_VPN_C2C_TRAFFIC", "no" } });
vars.append({ { "$PRIMARY_SERVER_DNS", m_settings->primaryDns() } });
vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } });
// Sftp vars
vars.append({ { "$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) } });
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
// Amnezia wireguard vars
vars.append({ { "$AWG_SUBNET_IP",
amneziaWireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
vars.append({ { "$INIT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
vars.append({ { "$SPECIAL_JUNK_1", amneziaWireguarConfig.value(config_key::specialJunk1).toString() } });
vars.append({ { "$SPECIAL_JUNK_2", amneziaWireguarConfig.value(config_key::specialJunk2).toString() } });
vars.append({ { "$SPECIAL_JUNK_3", amneziaWireguarConfig.value(config_key::specialJunk3).toString() } });
vars.append({ { "$SPECIAL_JUNK_4", amneziaWireguarConfig.value(config_key::specialJunk4).toString() } });
vars.append({ { "$SPECIAL_JUNK_5", amneziaWireguarConfig.value(config_key::specialJunk5).toString() } });
// Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key::userName).toString();
auto password = socks5ProxyConfig.value(config_key::password).toString();
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({ { "$SOCKS5_USER", socks5user } });
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
QString serverIp = (!ContainerProps::isAwgContainer(container) &&
container != DockerContainer::WireGuard && container != DockerContainer::Xray)
? NetworkUtilities::getIPAddress(credentials.hostName)
: credentials.hostName;
if (!serverIp.isEmpty()) {
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
} else {
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
}
return vars;
}
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode &errorCode)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
errorCode = runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
return stdOut;
}
void ServerController::cancelInstallation()
{
m_cancelInstallation = true;
}
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
{
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
}
QString ServerController::replaceVars(const QString &script, const Vars &vars)
{
QString s = script;
for (const QPair<QString, QString> &var : vars) {
s.replace(var.first, var.second);
}
return s;
}
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
if (container == DockerContainer::Dns) {
return ErrorCode::NoError;
}
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
const Proto protocol = ContainerProps::defaultProtocol(container);
const QString containerString = ProtocolProps::protoToString(protocol);
const QJsonObject containerConfig = config.value(containerString).toObject();
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
QString defaultPort("%1");
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
// TODO reimplement with netstat
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port);
}
if (transportProto == "tcpandudp") {
QString tcpProtoScript = script;
QString udpProtoScript = script;
tcpProtoScript.append("' | grep -i tcp");
udpProtoScript.append("' | grep -i udp");
tcpProtoScript.append(" | grep LISTEN");
ErrorCode errorCode =
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
errorCode = runScript(credentials, replaceVars(udpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
if (!stdOut.isEmpty()) {
return ErrorCode::ServerPortAlreadyAllocatedError;
}
return ErrorCode::NoError;
}
script = script.append("' | grep -i %1").arg(transportProto);
if (transportProto == "tcp") {
script = script.append(" | grep LISTEN");
}
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
if (!stdOut.isEmpty()) {
return ErrorCode::ServerPortAlreadyAllocatedError;
}
return ErrorCode::NoError;
}
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
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"))
return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required"))
return ErrorCode::ServerUserPasswordRequired;
return error;
}
ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container)
{
m_cancelInstallation = false;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
QFutureWatcher<ErrorCode> watcher;
QFuture<ErrorCode> future = QtConcurrent::run([this, &stdOut, &cbReadStdOut, &cbReadStdErr, &credentials]() {
// max 100 attempts
for (int i = 0; i < 30; ++i) {
if (m_cancelInstallation) {
return ErrorCode::ServerCancelInstallation;
}
stdOut.clear();
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("Packet manager not found"))
return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
return ErrorCode::NoError;
if (stdOut.isEmpty()) {
return ErrorCode::NoError;
} else {
#ifdef MZ_DEBUG
qDebug().noquote() << stdOut;
#endif
emit serverIsBusy(true);
QThread::msleep(10000);
}
}
return ErrorCode::ServerPacketManagerError;
});
QEventLoop wait;
QObject::connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future);
wait.exec();
emit serverIsBusy(false);
return future.result();
}
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
const std::function<QString()> &callback)
{
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
return error;
}

View File

@@ -0,0 +1,87 @@
#ifndef SERVERCONTROLLER_H
#define SERVERCONTROLLER_H
#include <QJsonObject>
#include <QObject>
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/sshclient.h"
class Settings;
class VpnConfigurator;
using namespace amnezia;
class ServerController : public QObject
{
Q_OBJECT
public:
ServerController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
~ServerController();
typedef QList<QPair<QString, QString>> Vars;
ErrorCode rebootServer(const ServerCredentials &credentials);
ErrorCode removeAllContainers(const ServerCredentials &credentials);
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate = false);
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
QJsonObject &newConfig);
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config = QJsonObject());
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path,
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
ErrorCode &errorCode);
QString replaceVars(const QString &script, const Vars &vars);
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
const QJsonObject &config = QJsonObject());
ErrorCode runScript(const ServerCredentials &credentials, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode &errorCode);
void cancelInstallation();
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
const std::function<QString()> &callback);
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config = QJsonObject());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
bool m_cancelInstallation = false;
libssh::Client m_sshClient;
signals:
void serverIsBusy(const bool isBusy);
};
#endif // SERVERCONTROLLER_H

View File

@@ -1,205 +0,0 @@
#include "serversController.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <AmneziaVPN-Swift.h>
#endif
ServersController::ServersController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent)
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
{
recomputeGatewayStacks();
}
void ServersController::addServer(const ServerConfig &server)
{
m_serversRepository->addServer(server);
}
void ServersController::editServer(int index, const ServerConfig &server)
{
m_serversRepository->editServer(index, server);
}
void ServersController::removeServer(int index)
{
m_serversRepository->removeServer(index);
}
void ServersController::setDefaultServerIndex(int index)
{
m_serversRepository->setDefaultServer(index);
}
void ServersController::setDefaultContainer(int serverIndex, DockerContainer container)
{
m_serversRepository->setDefaultContainer(serverIndex, container);
}
void ServersController::updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
{
m_serversRepository->setContainerConfig(serverIndex, container, config);
}
void ServersController::clearCachedProfile(int serverIndex, DockerContainer container)
{
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
}
QJsonArray ServersController::getServersArray() const
{
QJsonArray result;
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& server : servers) {
result.append(server.toJson());
}
return result;
}
QVector<ServerConfig> ServersController::getServers() const
{
return m_serversRepository->servers();
}
ContainerConfig ServersController::getContainerConfig(int serverIndex, DockerContainer container) const
{
return m_serversRepository->containerConfig(serverIndex, container);
}
int ServersController::getDefaultServerIndex() const
{
return m_serversRepository->defaultServerIndex();
}
int ServersController::getServersCount() const
{
return m_serversRepository->serversCount();
}
ServerConfig ServersController::getServerConfig(int serverIndex) const
{
return m_serversRepository->server(serverIndex);
}
ServerCredentials ServersController::getServerCredentials(int serverIndex) const
{
return m_serversRepository->serverCredentials(serverIndex);
}
QPair<QString, QString> ServersController::getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const
{
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
return serverConfig.getDnsPair(isAmneziaDnsEnabled,
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
}
ServersController::GatewayStacksData ServersController::gatewayStacks() const
{
return m_gatewayStacks;
}
void ServersController::recomputeGatewayStacks()
{
GatewayStacksData computed;
bool hasNewTags = false;
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& serverConfig : servers) {
if (serverConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) continue;
const QString userCountryCode = apiV2->apiConfig.userCountryCode;
const QString serviceType = apiV2->serviceType();
if (!userCountryCode.isEmpty()) {
if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) {
hasNewTags = true;
}
computed.userCountryCodes.insert(userCountryCode);
}
if (!serviceType.isEmpty()) {
if (!m_gatewayStacks.serviceTypes.contains(serviceType)) {
hasNewTags = true;
}
computed.serviceTypes.insert(serviceType);
}
}
}
m_gatewayStacks = std::move(computed);
if (hasNewTags) {
emit gatewayStacksExpanded();
}
}
bool ServersController::GatewayStacksData::operator==(const GatewayStacksData &other) const
{
return userCountryCodes == other.userCountryCodes && serviceTypes == other.serviceTypes;
}
QJsonObject ServersController::GatewayStacksData::toJson() const
{
QJsonObject json;
QJsonArray userCountryCodesArray;
for (const QString &code : userCountryCodes) {
userCountryCodesArray.append(code);
}
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
QJsonArray serviceTypesArray;
for (const QString &type : serviceTypes) {
serviceTypesArray.append(type);
}
json[apiDefs::key::serviceType] = serviceTypesArray;
return json;
}
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const
{
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& serverConfig : servers) {
if (serverConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) return false;
if (apiV2->apiConfig.userCountryCode == userCountryCode
&& apiV2->serviceType() == serviceType
&& apiV2->serviceProtocol() == serviceProtocol) {
return true;
}
}
}
return false;
}
bool ServersController::hasInstalledContainers(int serverIndex) const
{
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
for (auto it = containers.begin(); it != containers.end(); ++it) {
DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
return true;
}
if (container == DockerContainer::SSXray) {
return true;
}
}
return false;
}

View File

@@ -1,96 +0,0 @@
#ifndef SERVERSCONTROLLER_H
#define SERVERSCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QSet>
#include <QVector>
#include <QPair>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
class SshSession;
class InstallController;
using namespace amnezia;
/**
* @brief Core business logic controller for server operations
*
* This controller contains pure business logic for managing servers.
*/
class ServersController : public QObject
{
Q_OBJECT
public:
struct GatewayStacksData
{
QSet<QString> userCountryCodes;
QSet<QString> serviceTypes;
bool isEmpty() const { return userCountryCodes.isEmpty() && serviceTypes.isEmpty(); }
bool operator==(const GatewayStacksData &other) const;
QJsonObject toJson() const;
};
public:
explicit ServersController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository = nullptr,
QObject *parent = nullptr);
~ServersController() = default;
// Server management
void addServer(const ServerConfig &server);
void editServer(int index, const ServerConfig &server);
void removeServer(int index);
void setDefaultServerIndex(int index);
// Container management
void setDefaultContainer(int serverIndex, DockerContainer container);
void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
// Cache management
void clearCachedProfile(int serverIndex, DockerContainer container);
// Getters
QJsonArray getServersArray() const;
QVector<ServerConfig> getServers() const;
int getDefaultServerIndex() const;
int getServersCount() const;
ServerConfig getServerConfig(int serverIndex) const;
ServerCredentials getServerCredentials(int serverIndex) const;
ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const;
QPair<QString, QString> getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const;
GatewayStacksData gatewayStacks() const;
// Validation
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
bool hasInstalledContainers(int serverIndex) const;
signals:
void gatewayStacksExpanded();
public slots:
void recomputeGatewayStacks();
private:
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
GatewayStacksData m_gatewayStacks;
};
#endif // SERVERSCONTROLLER_H

View File

@@ -1,366 +0,0 @@
#include "settingsController.h"
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QOperatingSystemVersion>
#include "version.h"
#include "ui/utils/qAutoStart.h"
#include "logger.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
QString getPlatformName()
{
#if defined(Q_OS_WINDOWS)
return "Windows";
#elif defined(Q_OS_ANDROID)
return "Android";
#elif defined(Q_OS_LINUX)
return "Linux";
#elif defined(Q_OS_MACX)
return "MacOS";
#elif defined(Q_OS_IOS)
return "iOS";
#else
return "Unknown";
#endif
}
SettingsController::SettingsController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject* parent)
: QObject(parent),
m_serversRepository(serversRepository),
m_appSettingsRepository(appSettingsRepository)
{
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
m_isDevModeEnabled = m_appSettingsRepository->isDevGatewayEnv();
}
void SettingsController::toggleAmneziaDns(bool enable)
{
m_appSettingsRepository->setUseAmneziaDns(enable);
}
bool SettingsController::isAmneziaDnsEnabled() const
{
return m_appSettingsRepository->useAmneziaDns();
}
QString SettingsController::getPrimaryDns() const
{
return m_appSettingsRepository->primaryDns();
}
void SettingsController::setPrimaryDns(const QString &dns)
{
m_appSettingsRepository->setPrimaryDns(dns);
}
QString SettingsController::getSecondaryDns() const
{
return m_appSettingsRepository->secondaryDns();
}
void SettingsController::setSecondaryDns(const QString &dns)
{
m_appSettingsRepository->setSecondaryDns(dns);
}
bool SettingsController::isLoggingEnabled() const
{
return m_appSettingsRepository->isSaveLogs();
}
void SettingsController::toggleLogging(bool enable)
{
m_appSettingsRepository->setSaveLogs(enable);
#ifndef Q_OS_ANDROID
if (!enable) {
Logger::deInit();
} else {
if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#endif
Logger::setServiceLogsEnabled(enable);
if (enable) {
m_appSettingsRepository->setLogEnableDate(QDateTime::currentDateTime());
}
}
void SettingsController::clearLogs()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs();
#else
Logger::clearLogs(false);
Logger::clearServiceLogs();
#endif
}
QByteArray SettingsController::backupAppConfig() const
{
QByteArray data = m_appSettingsRepository->backupAppConfig();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject config = doc.object();
config["AppPlatform"] = getPlatform();
config["Conf/autoStart"] = isAutoStartEnabled();
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
return QJsonDocument(config).toJson();
}
ErrorCode SettingsController::restoreAppConfigFromData(const QByteArray &data)
{
if (!m_appSettingsRepository->restoreAppConfig(data)) {
return ErrorCode::RestoreBackupInvalidError;
}
m_serversRepository->invalidateCache();
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
bool autoStart = false;
if (newConfigData.contains("Conf/autoStart")) {
autoStart = newConfigData["Conf/autoStart"].toBool();
}
toggleAutoStart(autoStart);
#endif
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
bool appSplittunnelingEnabled =
newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
emit appSplitTunnelingRouteModeChanged(static_cast<AppsRouteMode>(appSplitTunnelingRouteMode));
#if defined(Q_OS_WINDOWS)
emit appSplitTunnelingRouteModeChanged(AppsRouteMode::VpnAllExceptApps);
#endif
if (newConfigData.contains("AppPlatform")) {
if (newConfigData.value("AppPlatform").toString() != getPlatform()) {
emit appSplitTunnelingClearAppsList();
}
}
emit appSplitTunnelingToggled(appSplittunnelingEnabled);
#endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled =
newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
emit siteSplitTunnelingRouteModeChanged(static_cast<RouteMode>(siteSplitTunnelingRouteMode));
emit siteSplitTunnelingToggled(siteSplittunnelingEnabled);
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
m_appSettingsRepository->setAutoConnect(false);
m_appSettingsRepository->setStartMinimized(false);
m_appSettingsRepository->setKillSwitchEnabled(false);
m_appSettingsRepository->setStrictKillSwitchEnabled(false);
#endif
return ErrorCode::NoError;
}
QString SettingsController::getAppVersion() const
{
return m_appVersion;
}
void SettingsController::clearSettings()
{
int serverCount = m_serversRepository->serversCount();
m_appSettingsRepository->clearSettings();
m_serversRepository->setServersArray(QJsonArray());
m_serversRepository->setDefaultServer(0);
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
emit siteSplitTunnelingToggled(false);
emit appSplitTunnelingRouteModeChanged(AppsRouteMode::VpnAllExceptApps);
emit appSplitTunnelingToggled(false);
toggleAutoStart(false);
}
bool SettingsController::isAutoConnectEnabled() const
{
return m_appSettingsRepository->isAutoConnect();
}
void SettingsController::toggleAutoConnect(bool enable)
{
m_appSettingsRepository->setAutoConnect(enable);
}
bool SettingsController::isAutoStartEnabled() const
{
return Autostart::isAutostart();
}
void SettingsController::toggleAutoStart(bool enable)
{
Autostart::setAutostart(enable);
if (!enable) {
toggleStartMinimized(false);
}
}
bool SettingsController::isStartMinimizedEnabled() const
{
return m_appSettingsRepository->isStartMinimized();
}
void SettingsController::toggleStartMinimized(bool enable)
{
m_appSettingsRepository->setStartMinimized(enable);
}
bool SettingsController::isScreenshotsEnabled() const
{
return m_appSettingsRepository->isScreenshotsEnabled();
}
void SettingsController::toggleScreenshotsEnabled(bool enable)
{
m_appSettingsRepository->setScreenshotsEnabled(enable);
}
bool SettingsController::isNewsNotificationsEnabled() const
{
return m_appSettingsRepository->isNewsNotifications();
}
void SettingsController::toggleNewsNotificationsEnabled(bool enable)
{
m_appSettingsRepository->setNewsNotifications(enable);
}
bool SettingsController::isKillSwitchEnabled() const
{
return m_appSettingsRepository->isKillSwitchEnabled();
}
void SettingsController::toggleKillSwitch(bool enable)
{
m_appSettingsRepository->setKillSwitchEnabled(enable);
}
bool SettingsController::isStrictKillSwitchEnabled() const
{
return m_appSettingsRepository->isStrictKillSwitchEnabled();
}
void SettingsController::toggleStrictKillSwitch(bool enable)
{
m_appSettingsRepository->setStrictKillSwitchEnabled(enable);
}
QString SettingsController::getInstallationUuid(bool createIfNotExists) const
{
return m_appSettingsRepository->getInstallationUuid(createIfNotExists);
}
void SettingsController::enableDevMode()
{
m_isDevModeEnabled = true;
}
bool SettingsController::isDevModeEnabled() const
{
return m_isDevModeEnabled;
}
void SettingsController::resetGatewayEndpoint()
{
m_appSettingsRepository->resetGatewayEndpoint();
}
void SettingsController::setGatewayEndpoint(const QString &endpoint)
{
m_appSettingsRepository->setGatewayEndpoint(endpoint);
}
QString SettingsController::getGatewayEndpoint() const
{
return m_appSettingsRepository->isDevGatewayEnv() ? "Dev endpoint" : m_appSettingsRepository->getGatewayEndpoint();
}
bool SettingsController::isDevGatewayEnv() const
{
return m_appSettingsRepository->isDevGatewayEnv();
}
void SettingsController::toggleDevGatewayEnv(bool enabled)
{
m_appSettingsRepository->toggleDevGatewayEnv(enabled);
if (enabled) {
m_appSettingsRepository->setDevGatewayEndpoint();
} else {
m_appSettingsRepository->resetGatewayEndpoint();
}
}
bool SettingsController::isHomeAdLabelVisible() const
{
return m_appSettingsRepository->isHomeAdLabelVisible();
}
void SettingsController::disableHomeAdLabel()
{
m_appSettingsRepository->disableHomeAdLabel();
}
void SettingsController::checkIfNeedDisableLogs()
{
if (m_appSettingsRepository->isSaveLogs()) {
m_loggingDisableDate = m_appSettingsRepository->getLogEnableDate().addDays(14);
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
toggleLogging(false);
clearLogs();
}
}
}
QString SettingsController::getPlatform() const
{
return getPlatformName();
}
QLocale SettingsController::getAppLanguage() const
{
return m_appSettingsRepository->getAppLanguage();
}
void SettingsController::setAppLanguage(const QLocale &locale)
{
m_appSettingsRepository->setAppLanguage(locale);
}
bool SettingsController::isPremV1MigrationReminderActive() const
{
return m_appSettingsRepository->isPremV1MigrationReminderActive();
}
void SettingsController::disablePremV1MigrationReminder()
{
m_appSettingsRepository->disablePremV1MigrationReminder();
}
QString SettingsController::nextAvailableServerName() const
{
return m_appSettingsRepository->nextAvailableServerName();
}

View File

@@ -1,112 +0,0 @@
#ifndef SETTINGSCONTROLLER_H
#define SETTINGSCONTROLLER_H
#include <QObject>
#include <QJsonObject>
#include <QByteArray>
#include <QDateTime>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
using namespace amnezia;
class SettingsController : public QObject
{
Q_OBJECT
public:
explicit SettingsController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject* parent = nullptr);
~SettingsController() = default;
void toggleAmneziaDns(bool enable);
bool isAmneziaDnsEnabled() const;
QString getPrimaryDns() const;
void setPrimaryDns(const QString &dns);
QString getSecondaryDns() const;
void setSecondaryDns(const QString &dns);
bool isLoggingEnabled() const;
void toggleLogging(bool enable);
void clearLogs();
QByteArray backupAppConfig() const;
ErrorCode restoreAppConfigFromData(const QByteArray &data);
QString getAppVersion() const;
void clearSettings();
bool isAutoConnectEnabled() const;
void toggleAutoConnect(bool enable);
bool isAutoStartEnabled() const;
void toggleAutoStart(bool enable);
bool isStartMinimizedEnabled() const;
void toggleStartMinimized(bool enable);
bool isScreenshotsEnabled() const;
void toggleScreenshotsEnabled(bool enable);
bool isNewsNotificationsEnabled() const;
void toggleNewsNotificationsEnabled(bool enable);
bool isKillSwitchEnabled() const;
void toggleKillSwitch(bool enable);
bool isStrictKillSwitchEnabled() const;
void toggleStrictKillSwitch(bool enable);
QString getInstallationUuid(bool createIfNotExists = true) const;
void enableDevMode();
bool isPremV1MigrationReminderActive() const;
void disablePremV1MigrationReminder();
QString nextAvailableServerName() const;
bool isDevModeEnabled() const;
void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint);
QString getGatewayEndpoint() const;
bool isDevGatewayEnv() const;
void toggleDevGatewayEnv(bool enabled);
bool isHomeAdLabelVisible() const;
void disableHomeAdLabel();
void checkIfNeedDisableLogs();
QLocale getAppLanguage() const;
void setAppLanguage(const QLocale &locale);
signals:
void siteSplitTunnelingRouteModeChanged(RouteMode mode);
void siteSplitTunnelingToggled(bool enabled);
void appSplitTunnelingRouteModeChanged(AppsRouteMode mode);
void appSplitTunnelingToggled(bool enabled);
void appSplitTunnelingClearAppsList();
private:
QString getPlatform() const;
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
QString m_appVersion;
QDateTime m_loggingDisableDate;
bool m_isDevModeEnabled = false;
};
#endif

View File

@@ -0,0 +1,146 @@
#include "vpnConfigurationController.h"
#include "configurators/awg_configurator.h"
#include "configurators/cloak_configurator.h"
#include "configurators/ikev2_configurator.h"
#include "configurators/openvpn_configurator.h"
#include "configurators/shadowsocks_configurator.h"
#include "configurators/wireguard_configurator.h"
#include "configurators/xray_configurator.h"
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings,
QSharedPointer<ServerController> serverController, QObject *parent)
: QObject { parent }, m_settings(settings), m_serverController(serverController)
{
}
QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator(const Proto protocol)
{
switch (protocol) {
case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(m_settings, m_serverController));
case Proto::ShadowSocks: return QScopedPointer<ConfiguratorBase>(new ShadowSocksConfigurator(m_settings, m_serverController));
case Proto::Cloak: return QScopedPointer<ConfiguratorBase>(new CloakConfigurator(m_settings, m_serverController));
case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(m_settings, m_serverController, false));
case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(m_settings, m_serverController));
case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(m_settings, m_serverController));
case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings, m_serverController));
case Proto::SSXray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings, m_serverController));
default: return QScopedPointer<ConfiguratorBase>();
}
}
ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const ServerCredentials &credentials,
const DockerContainer container, QJsonObject &containerConfig)
{
ErrorCode errorCode = ErrorCode::NoError;
if (ContainerProps::containerService(container) == ServiceType::Other) {
return errorCode;
}
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
auto configurator = createConfigurator(protocol);
QString protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
protocolConfig.insert(config_key::last_config, protocolConfigString);
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
}
return errorCode;
}
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
const ServerCredentials &credentials, const DockerContainer container,
const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString)
{
ErrorCode errorCode = ErrorCode::NoError;
if (ContainerProps::containerService(container) == ServiceType::Other) {
return errorCode;
}
auto configurator = createConfigurator(protocol);
protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
protocolConfigString = configurator->processConfigWithExportSettings(dns, isApiConfig, protocolConfigString);
return errorCode;
}
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container)
{
QJsonObject vpnConfiguration {};
if (ContainerProps::containerService(container) == ServiceType::Other) {
return vpnConfiguration;
}
bool isApiConfig = serverConfig.value(config_key::configVersion).toInt();
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
if (isApiConfig && container == DockerContainer::Cloak && proto == ProtocolEnumNS::Proto::ShadowSocks) {
continue;
}
QString protocolConfigString =
containerConfig.value(ProtocolProps::protoToString(proto)).toObject().value(config_key::last_config).toString();
auto configurator = createConfigurator(proto);
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (ContainerProps::isAwgContainer(container) || container == DockerContainer::WireGuard) {
// add mtu for old configs
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
vpnConfigData[config_key::mtu] =
ContainerProps::isAwgContainer(container) ? protocols::awg::defaultMtu :
protocols::wireguard::defaultMtu;
}
}
vpnConfiguration.insert(ProtocolProps::key_proto_config_data(proto), vpnConfigData);
}
Proto proto = ContainerProps::defaultProtocol(container);
vpnConfiguration[config_key::vpnproto] = ProtocolProps::protoToString(proto);
vpnConfiguration[config_key::dns1] = dns.first;
vpnConfiguration[config_key::dns2] = dns.second;
vpnConfiguration[config_key::hostName] = serverConfig.value(config_key::hostName).toString();
vpnConfiguration[config_key::description] = serverConfig.value(config_key::description).toString();
vpnConfiguration[config_key::configVersion] = serverConfig.value(config_key::configVersion).toInt();
// TODO: try to get hostName, port, description for 3rd party configs
// vpnConfiguration[config_key::port] = ...;
return vpnConfiguration;
}
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig,
const QString &stdOut)
{
Proto mainProto = ContainerProps::defaultProtocol(container);
if (container == DockerContainer::TorWebSite) {
QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
qDebug() << "amnezia-tor onions" << stdOut;
QString onion = stdOut;
onion.replace("\n", "");
protocol.insert(config_key::site, onion);
containerConfig.insert(ProtocolProps::protoToString(mainProto), protocol);
}
}

View File

@@ -0,0 +1,37 @@
#ifndef VPNCONFIGIRATIONSCONTROLLER_H
#define VPNCONFIGIRATIONSCONTROLLER_H
#include <QObject>
#include "configurators/configurator_base.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "settings.h"
class VpnConfigurationsController : public QObject
{
Q_OBJECT
public:
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController,
QObject *parent = nullptr);
public slots:
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
QJsonObject &containerConfig);
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container);
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
signals:
private:
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;
};
#endif // VPNCONFIGIRATIONSCONTROLLER_H

View File

@@ -1,11 +1,38 @@
#ifndef ERRORCODES_H
#define ERRORCODES_H
#ifndef DEFS_H
#define DEFS_H
#include <QMetaEnum>
#include <QObject>
namespace amnezia
{
struct ServerCredentials
{
QString hostName;
QString userName;
QString secretData;
int port = 22;
bool isValid() const
{
return !hostName.isEmpty() && !userName.isEmpty() && !secretData.isEmpty() && port > 0;
}
};
struct InstalledAppInfo {
QString appName;
QString packageName;
QString appPath;
bool operator==(const InstalledAppInfo& other) const {
if (!packageName.isEmpty()) {
return packageName == other.packageName;
} else {
return appPath == other.appPath;
}
}
};
namespace error_code_ns
{
Q_NAMESPACE
@@ -53,6 +80,8 @@ namespace amnezia
// Distro errors
OpenVpnExecutableMissing = 600,
ShadowSocksExecutableMissing = 601,
CloakExecutableMissing = 602,
AmneziaServiceConnectionFailed = 603,
ExecutableMissing = 604,
XrayExecutableMissing = 605,
@@ -66,13 +95,13 @@ namespace amnezia
// 3rd party utils errors
OpenSslFailed = 800,
ShadowSocksExecutableCrashed = 801,
CloakExecutableCrashed = 802,
XrayExecutableCrashed = 803,
Tun2SockExecutableCrashed = 804,
// import and install errors
ImportInvalidConfigError = 900,
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
ImportOpenConfigError = 901,
NoInstalledContainersError = 902,
@@ -94,9 +123,6 @@ namespace amnezia
ApiUpdateRequestError = 1111,
ApiSubscriptionExpiredError = 1112,
ApiPurchaseError = 1113,
ApiSubscriptionNotActiveError = 1114,
ApiNoPurchasedSubscriptionsError = 1115,
ApiTrialAlreadyUsedError = 1116,
// QFile errors
OpenError = 1200,
@@ -110,10 +136,9 @@ namespace amnezia
}
using ErrorCode = error_code_ns::ErrorCode;
}
} // namespace amnezia
Q_DECLARE_METATYPE(amnezia::ErrorCode)
#endif // ERRORCODES_H
#endif // DEFS_H

View File

@@ -1,4 +1,4 @@
#include "errorStrings.h"
#include "errorstrings.h"
using namespace amnezia;
@@ -48,6 +48,8 @@ QString errorString(ErrorCode code) {
// Distro errors
case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break;
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("Shadowsocks (ss-local) executable missing"); break;
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
@@ -57,10 +59,8 @@ QString errorString(ErrorCode code) {
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break;
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
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::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;
case(ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;
// Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
@@ -80,9 +80,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break;
case (ErrorCode::ApiPurchaseError): errorMessage = QObject::tr("Unable to process purchase"); break;
case (ErrorCode::ApiSubscriptionNotActiveError): errorMessage = QObject::tr("No active subscription found"); break;
case (ErrorCode::ApiNoPurchasedSubscriptionsError): errorMessage = QObject::tr("No purchased subscriptions found. Please purchase a subscription first"); break;
case (ErrorCode::ApiTrialAlreadyUsedError): errorMessage = QObject::tr("This email address has already been used to activate a trial"); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View File

@@ -3,7 +3,7 @@
#include <QDebug>
#include "core/utils/errorCodes.h"
#include "defs.h"
using namespace amnezia;

View File

@@ -1,200 +0,0 @@
#include "awgInstaller.h"
#include <QPair>
#include <QRandomGenerator>
#include <QSet>
#include <QStringList>
#include <QVector>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/utilities.h"
#include "core/models/protocols/awgProtocolConfig.h"
using namespace amnezia;
using namespace ProtocolUtils;
AwgInstaller::AwgInstaller(QObject *parent)
: InstallerBase(parent)
{
}
ContainerConfig AwgInstaller::generateConfig(DockerContainer container, int port, TransportProto transportProto)
{
ContainerConfig config = createBaseConfig(container, port, transportProto);
bool isAwg2 = (container == DockerContainer::Awg2);
if (auto* awgConfig = config.getAwgProtocolConfig()) {
generateAwgParameters(awgConfig->serverConfig, isAwg2);
if (isAwg2) {
awgConfig->serverConfig.protocolVersion = "2";
}
}
return config;
}
void AwgInstaller::generateAwgParameters(AwgServerConfig &serverConfig, bool isAwg2)
{
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50);
int s1 = QRandomGenerator::global()->bounded(15, 150);
int s2 = QRandomGenerator::global()->bounded(15, 150);
int s3 = QRandomGenerator::global()->bounded(0, 64);
int s4 = QRandomGenerator::global()->bounded(0, 20);
// Ensure all values are unique and don't create equal packet sizes
QSet<int> usedValues;
usedValues.insert(s1);
while (usedValues.contains(s2) || s1 + amnezia::AwgConstant::messageInitiationSize == s2 + amnezia::AwgConstant::messageResponseSize) {
s2 = QRandomGenerator::global()->bounded(15, 150);
}
usedValues.insert(s2);
while (usedValues.contains(s3) || s1 + amnezia::AwgConstant::messageInitiationSize == s3 + amnezia::AwgConstant::messageCookieReplySize
|| s2 + amnezia::AwgConstant::messageResponseSize == s3 + amnezia::AwgConstant::messageCookieReplySize) {
s3 = QRandomGenerator::global()->bounded(0, 64);
}
usedValues.insert(s3);
while (usedValues.contains(s4)) {
s4 = QRandomGenerator::global()->bounded(0, 20);
}
QString initPacketJunkSize = QString::number(s1);
QString responsePacketJunkSize = QString::number(s2);
QString cookieReplyPacketJunkSize = QString::number(s3);
QString transportPacketJunkSize = QString::number(s4);
QString initPacketMagicHeader;
QString responsePacketMagicHeader;
QString underloadPacketMagicHeader;
QString transportPacketMagicHeader;
if (isAwg2) {
// AWG 2.0: use range format for magic headers
QVector<QPair<QString, QString>> headersValue;
int min = 5;
auto max = (std::numeric_limits<qint32>::max)();
while (headersValue.size() != 4) {
auto first = QRandomGenerator::global()->bounded(min, max);
auto second = QRandomGenerator::global()->bounded(first, max);
min = second;
headersValue.push_back(QPair<QString, QString>(QString::number(first), QString::number(second)));
}
initPacketMagicHeader = headersValue.at(0).first + "-" + headersValue.at(0).second;
responsePacketMagicHeader = headersValue.at(1).first + "-" + headersValue.at(1).second;
underloadPacketMagicHeader = headersValue.at(2).first + "-" + headersValue.at(2).second;
transportPacketMagicHeader = headersValue.at(3).first + "-" + headersValue.at(3).second;
} else {
// AWG legacy: use single values for magic headers
QSet<QString> headersValue;
while (headersValue.size() != 4) {
auto max = (std::numeric_limits<qint32>::max)();
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(5, max)));
}
auto headersValueList = headersValue.values();
initPacketMagicHeader = headersValueList.at(0);
responsePacketMagicHeader = headersValueList.at(1);
underloadPacketMagicHeader = headersValueList.at(2);
transportPacketMagicHeader = headersValueList.at(3);
}
serverConfig.junkPacketCount = junkPacketCount;
serverConfig.junkPacketMinSize = junkPacketMinSize;
serverConfig.junkPacketMaxSize = junkPacketMaxSize;
serverConfig.initPacketJunkSize = initPacketJunkSize;
serverConfig.responsePacketJunkSize = responsePacketJunkSize;
serverConfig.initPacketMagicHeader = initPacketMagicHeader;
serverConfig.responsePacketMagicHeader = responsePacketMagicHeader;
serverConfig.underloadPacketMagicHeader = underloadPacketMagicHeader;
serverConfig.transportPacketMagicHeader = transportPacketMagicHeader;
serverConfig.cookieReplyPacketJunkSize = cookieReplyPacketJunkSize;
serverConfig.transportPacketJunkSize = transportPacketJunkSize;
serverConfig.specialJunk1 = protocols::awg::defaultSpecialJunk1;
serverConfig.specialJunk2 = protocols::awg::defaultSpecialJunk2;
serverConfig.specialJunk3 = protocols::awg::defaultSpecialJunk3;
serverConfig.specialJunk4 = protocols::awg::defaultSpecialJunk4;
serverConfig.specialJunk5 = protocols::awg::defaultSpecialJunk5;
}
ErrorCode AwgInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, ContainerConfig &config)
{
ErrorCode errorCode = ErrorCode::NoError;
// Use appropriate config path based on container type
QString configPath = protocols::awg::serverConfigPath;
if (container == DockerContainer::Awg) {
configPath = protocols::awg::serverLegacyConfigPath;
}
QString serverConfig = sshSession->getTextFileFromContainer(container, credentials, configPath, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
if (auto* awgConfig = config.getAwgProtocolConfig()) {
QString addressValue = serverConfigMap.value("Address");
QStringList addressParts = addressValue.split("/");
awgConfig->serverConfig.subnetAddress = addressParts.value(0);
if (addressParts.size() > 1) {
awgConfig->serverConfig.subnetCidr = addressParts.value(1);
}
awgConfig->serverConfig.junkPacketCount = serverConfigMap.value(configKey::junkPacketCount);
awgConfig->serverConfig.junkPacketMinSize = serverConfigMap.value(configKey::junkPacketMinSize);
awgConfig->serverConfig.junkPacketMaxSize = serverConfigMap.value(configKey::junkPacketMaxSize);
awgConfig->serverConfig.initPacketJunkSize = serverConfigMap.value(configKey::initPacketJunkSize);
awgConfig->serverConfig.responsePacketJunkSize = serverConfigMap.value(configKey::responsePacketJunkSize);
awgConfig->serverConfig.initPacketMagicHeader = serverConfigMap.value(configKey::initPacketMagicHeader);
awgConfig->serverConfig.responsePacketMagicHeader = serverConfigMap.value(configKey::responsePacketMagicHeader);
awgConfig->serverConfig.underloadPacketMagicHeader = serverConfigMap.value(configKey::underloadPacketMagicHeader);
awgConfig->serverConfig.transportPacketMagicHeader = serverConfigMap.value(configKey::transportPacketMagicHeader);
// hack to parse i1-i5 from commented lines in server config
awgConfig->serverConfig.specialJunk1 = serverConfigMap.value(QString("# ") + configKey::specialJunk1);
awgConfig->serverConfig.specialJunk2 = serverConfigMap.value(QString("# ") + configKey::specialJunk2);
awgConfig->serverConfig.specialJunk3 = serverConfigMap.value(QString("# ") + configKey::specialJunk3);
awgConfig->serverConfig.specialJunk4 = serverConfigMap.value(QString("# ") + configKey::specialJunk4);
awgConfig->serverConfig.specialJunk5 = serverConfigMap.value(QString("# ") + configKey::specialJunk5);
// AWG 2.0 specific fields
if (container == DockerContainer::Awg2) {
awgConfig->serverConfig.protocolVersion = "2";
awgConfig->serverConfig.cookieReplyPacketJunkSize = serverConfigMap.value(configKey::cookieReplyPacketJunkSize);
awgConfig->serverConfig.transportPacketJunkSize = serverConfigMap.value(configKey::transportPacketJunkSize);
}
}
return ErrorCode::NoError;
}

View File

@@ -1,21 +0,0 @@
#ifndef AWGINSTALLER_H
#define AWGINSTALLER_H
#include "installerBase.h"
class AwgInstaller : public InstallerBase
{
Q_OBJECT
public:
explicit AwgInstaller(QObject *parent = nullptr);
amnezia::ContainerConfig generateConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto) override;
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
SshSession* serverController, amnezia::ContainerConfig &config) override;
private:
void generateAwgParameters(amnezia::AwgServerConfig &serverConfig, bool isAwg2 = false);
};
#endif // AWGINSTALLER_H

Some files were not shown because too many files have changed in this diff Show More