mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-02 08:33:38 +02:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b03acc95c4 | |||
| 24759c92ad | |||
| 9e92ee020e | |||
| 7a4f6b628b | |||
| 7e2f223d7f | |||
| eb48e4b668 | |||
| 0b0766ce8b | |||
| 26825c2898 | |||
| 9ace09a604 | |||
| 702735c2ca | |||
| 174f2ac3db | |||
| e3b5b4a9d9 | |||
| 72ba012765 | |||
| 0f9bbcd060 | |||
| a9d038d8bf | |||
| bf67e3491e | |||
| 856bc23b5d | |||
| 54a6845315 | |||
| 0c7059a476 | |||
| 5bed92ab0b | |||
| 49a14785c6 | |||
| 2c78c06dda | |||
| cf8a0efd0d | |||
| 5211cdd4c0 | |||
| d10aa43d8b | |||
| 6b0f1ed429 | |||
| 4bde1ccb44 | |||
| 03c18c44e2 | |||
| 72ffc7ce6a | |||
| 87b738ef16 | |||
| b868831bcb | |||
| 477d7214c5 | |||
| f3cd3d4f06 | |||
| aea4cc2389 | |||
| 245aa8eb8c | |||
| f14a2add0f | |||
| 89703ba58f | |||
| 23715fca8b | |||
| d90685600e | |||
| f007e5eb5c | |||
| a8ccea00c7 | |||
| cd2ee00769 | |||
| c98a418807 | |||
| 0e4ae26bae | |||
| d50e7dd3f4 | |||
| f0085f52eb | |||
| 5c19b08e5e | |||
| 79edbe52a3 | |||
| 0dd181bb5b | |||
| d8682003fa | |||
| f4a2cf9984 | |||
| 98e6358fd3 | |||
| af90065d2e | |||
| f372f4074b | |||
| 6a2e5f83a1 | |||
| a2badd46c4 | |||
| 8623a983b8 | |||
| 151e662027 | |||
| f588fe29db | |||
| 030b0351a2 | |||
| d4453a5f38 | |||
| 2252905596 | |||
| ec650a65f7 | |||
| 6953f8d814 | |||
| 624a84cbfb | |||
| 506d9793e1 | |||
| ef52f6ab08 | |||
| 5312a6e885 | |||
| fdd600794e | |||
| 7bfbdca72a | |||
| a22c08a41d | |||
| 10ea9b418a | |||
| e39efb1d68 | |||
| 7db84122f9 | |||
| 84ad167ab4 | |||
| ed7e217a6b | |||
| c1b0d4a4a7 | |||
| 2f84e24353 | |||
| f73586185b | |||
| 653ffb9a68 | |||
| fe8c2d157a | |||
| 86367a1276 | |||
| b0fcf92ada | |||
| 283b6ebf81 | |||
| d0a7fc5116 | |||
| 9851aacba7 | |||
| 51f9fb9e0a | |||
| 0325761f3e | |||
| a6ca1b12da | |||
| 82a9e7e27d | |||
| f5301e1315 | |||
| adab30fc81 | |||
| e7bd24f065 | |||
| 2ec448ba13 | |||
| c6e6f2ae84 | |||
| e9468a4c2f | |||
| 45de951897 | |||
| db8d966fac | |||
| 6b69bc9618 | |||
| 0089b0b799 | |||
| e4841e809b | |||
| ba4237f1dd | |||
| f6acec53c0 | |||
| 5f631eaa76 | |||
| 7730dd510c | |||
| 30bd264f17 | |||
| 5206665fa0 | |||
| 073491ccb4 | |||
| 561b62cd40 | |||
| 1284ed4d84 | |||
| 6f34443191 | |||
| 02f186c54e | |||
| 784c6cf585 | |||
| 14f132e127 | |||
| 9cb624e681 | |||
| 516e3da7e2 | |||
| 0e83586cae | |||
| 95bdae68f4 | |||
| 294778884b | |||
| 10caecbffd | |||
| 553a6a73dd | |||
| e646b85e56 | |||
| b7c513c05f | |||
| 9f82b4c21f | |||
| 02b2da38cf | |||
| f51077b2be | |||
| 33f49bfddb | |||
| 9a81f13f81 | |||
| 915fb6759a | |||
| c5a5bfde69 | |||
| 0a90fd110d | |||
| 541d6eb0b8 | |||
| d443a0063d | |||
| f0c6edb670 | |||
| 9189b53a0d | |||
| fceccaefcc | |||
| fbeabf43ca | |||
| 78c7893f90 | |||
| cb9a25006c |
@@ -233,7 +233,7 @@ jobs:
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '13.4'
|
||||
xcode-version: '14.3.1'
|
||||
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.4.1.2
|
||||
project(${PROJECT} VERSION 4.5.3.0
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
@@ -11,7 +11,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 47)
|
||||
set(APP_ANDROID_VERSION_CODE 52)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
+1
-1
Submodule client/3rd-prebuilt updated: 2fa21880b9...c969f28b84
Vendored
+1
-1
Submodule client/3rd/OpenVPNAdapter updated: 6f71d0743d...7c821a8d5c
@@ -72,7 +72,7 @@ namespace QSimpleCrypto
|
||||
/// \param notAfter - X509 end date.
|
||||
/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
|
||||
///
|
||||
X509* generateSelfSignedCertificate(const RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
|
||||
X509* generateSelfSignedCertificate(RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
|
||||
const QByteArray& certificateFileName = "", const EVP_MD* md = EVP_sha512(),
|
||||
const long& serialNumber = 1, const long& version = x509LastVersion,
|
||||
const long& notBefore = 0, const long& notAfter = oneYear);
|
||||
|
||||
@@ -139,7 +139,7 @@ X509* QSimpleCrypto::QX509::verifyCertificate(X509* x509, X509_STORE* store)
|
||||
/// \param notAfter - X509 end date.
|
||||
/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
|
||||
///
|
||||
X509* QSimpleCrypto::QX509::generateSelfSignedCertificate(const RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
|
||||
X509* QSimpleCrypto::QX509::generateSelfSignedCertificate(RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
|
||||
const QByteArray& certificateFileName, const EVP_MD* md,
|
||||
const long& serialNumber, const long& version,
|
||||
const long& notBefore, const long& notAfter)
|
||||
|
||||
+13
-2
@@ -63,11 +63,14 @@ qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(AMNEZIAVPN_TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru_RU.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar_EG.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_my_MM.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_uk_UA.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ur_PK.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_hi_IN.ts
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||
@@ -119,7 +122,9 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||
@@ -129,6 +134,7 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h
|
||||
)
|
||||
|
||||
# Mozilla headres
|
||||
@@ -158,12 +164,15 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp
|
||||
)
|
||||
|
||||
# Mozilla sources
|
||||
@@ -293,6 +302,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
|
||||
)
|
||||
|
||||
@@ -304,6 +314,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "logger.h"
|
||||
#include "ui/models/installedAppsModel.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "platforms/ios/QRCodeReaderBase.h"
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include "core/installedAppsImageProvider.h"
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
@@ -30,8 +31,8 @@
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||
#else
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
|
||||
int timeout, const QString &userData)
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, int timeout,
|
||||
const QString &userData)
|
||||
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
|
||||
#endif
|
||||
{
|
||||
@@ -44,12 +45,12 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecond
|
||||
s.setValue("permFixed", true);
|
||||
}
|
||||
|
||||
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + ORGANIZATION_NAME + "/"
|
||||
+ APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
|
||||
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + ORGANIZATION_NAME + "/"
|
||||
+ APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
#endif
|
||||
|
||||
@@ -82,8 +83,7 @@ void AmneziaApplication::init()
|
||||
|
||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings));
|
||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||
m_vpnConnectionThread.start();
|
||||
|
||||
@@ -96,25 +96,20 @@ void AmneziaApplication::init()
|
||||
qFatal("Android logging initialization failed");
|
||||
}
|
||||
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
|
||||
connect(m_settings.get(), &Settings::saveLogsChanged,
|
||||
AndroidController::instance(), &AndroidController::setSaveLogs);
|
||||
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::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
||||
|
||||
connect(m_settings.get(), &Settings::serverRemoved,
|
||||
AndroidController::instance(), &AndroidController::resetLastServer);
|
||||
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
||||
|
||||
connect(m_settings.get(), &Settings::settingsCleared,
|
||||
[](){ AndroidController::instance()->resetLastServer(-1); });
|
||||
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();
|
||||
});
|
||||
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");
|
||||
}
|
||||
@@ -124,6 +119,8 @@ void AmneziaApplication::init()
|
||||
m_importController->extractConfigFromData(data);
|
||||
m_pageController->goToPageViewConfig();
|
||||
});
|
||||
|
||||
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
@@ -140,13 +137,9 @@ void AmneziaApplication::init()
|
||||
m_settingsController->importBackupFromOutside(filePath);
|
||||
});
|
||||
|
||||
QTimer::singleShot(0, this, [this](){
|
||||
AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled());
|
||||
});
|
||||
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
||||
|
||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) {
|
||||
AmneziaVPN::toggleScreenshots(enabled);
|
||||
});
|
||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
|
||||
#endif
|
||||
|
||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||
@@ -154,14 +147,12 @@ void AmneziaApplication::init()
|
||||
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::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
|
||||
&ConnectionController::openConnection);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionController::closeConnection);
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
|
||||
&NotificationHandler::onTranslationsUpdated);
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||
|
||||
m_engine->load(url);
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
@@ -234,7 +225,8 @@ void AmneziaApplication::registerTypes()
|
||||
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
|
||||
"ContainersModelFilters");
|
||||
|
||||
//
|
||||
qmlRegisterType<InstalledAppsModel>("InstalledAppsModel", 1, 0, "InstalledAppsModel");
|
||||
|
||||
Vpn::declareQmlVpnConnectionStateEnum();
|
||||
PageLoader::declareQmlPageEnum();
|
||||
}
|
||||
@@ -311,8 +303,7 @@ void AmneziaApplication::initModels()
|
||||
|
||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
|
||||
&ContainersModel::updateModel);
|
||||
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
||||
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
||||
&ContainersModel::updateModel);
|
||||
m_serversModel->resetModel();
|
||||
@@ -325,6 +316,9 @@ void AmneziaApplication::initModels()
|
||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||
|
||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||
|
||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||
|
||||
@@ -343,6 +337,9 @@ void AmneziaApplication::initModels()
|
||||
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.reset(new Ikev2ConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
||||
@@ -355,28 +352,27 @@ void AmneziaApplication::initModels()
|
||||
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
|
||||
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
|
||||
&ServersModel::clearCachedProfile);
|
||||
|
||||
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
|
||||
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||
ServerCredentials credentials) {
|
||||
m_serversModel->reloadDefaultServerContainerConfig();
|
||||
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
|
||||
emit m_configurator->clientModelUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
void AmneziaApplication::initControllers()
|
||||
{
|
||||
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
||||
m_connectionController.reset(
|
||||
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
|
||||
&ConnectionController::onTranslationsUpdated);
|
||||
connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](const QString &errorMessage) {
|
||||
emit m_pageController->showErrorMessage(errorMessage);
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
});
|
||||
connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
|
||||
&ConnectionController::toggleConnection, Qt::QueuedConnection);
|
||||
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
|
||||
|
||||
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
||||
|
||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
|
||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
||||
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
|
||||
&PageController::showPassphraseRequestDrawer);
|
||||
@@ -388,36 +384,23 @@ void AmneziaApplication::initControllers()
|
||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||
|
||||
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel,
|
||||
m_settings, m_configurator));
|
||||
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||
|
||||
m_settingsController.reset(
|
||||
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings));
|
||||
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
||||
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
||||
}
|
||||
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(),
|
||||
&ServersModel::toggleAmneziaDns);
|
||||
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
|
||||
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||
|
||||
m_systemController.reset(new SystemController(m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
|
||||
m_apiController.reset(new ApiController(m_serversModel, m_containersModel));
|
||||
m_engine->rootContext()->setContextProperty("ApiController", m_apiController.get());
|
||||
connect(m_apiController.get(), &ApiController::updateStarted, this,
|
||||
[this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Connecting); });
|
||||
connect(m_apiController.get(), &ApiController::errorOccurred, this, [this](const QString &errorMessage) {
|
||||
if (m_connectionController->isConnectionInProgress()) {
|
||||
emit m_pageController->showErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
});
|
||||
connect(m_apiController.get(), &ApiController::updateFinished, m_connectionController.get(),
|
||||
&ConnectionController::toggleConnection);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "settings.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
#include "configurators/vpn_configurator.h"
|
||||
|
||||
#include "ui/controllers/connectionController.h"
|
||||
#include "ui/controllers/exportController.h"
|
||||
#include "ui/controllers/importController.h"
|
||||
@@ -24,7 +22,7 @@
|
||||
#include "ui/controllers/settingsController.h"
|
||||
#include "ui/controllers/sitesController.h"
|
||||
#include "ui/controllers/systemController.h"
|
||||
#include "ui/controllers/apiController.h"
|
||||
#include "ui/controllers/appSplitTunnelingController.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
@@ -36,11 +34,13 @@
|
||||
#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/protocols_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
#include "ui/models/clientManagementModel.h"
|
||||
#include "ui/models/appSplitTunnelingModel.h"
|
||||
|
||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||
|
||||
@@ -83,7 +83,6 @@ private:
|
||||
|
||||
QQmlApplicationEngine *m_engine {};
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||
|
||||
QSharedPointer<ContainerProps> m_containerProps;
|
||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||
@@ -97,11 +96,13 @@ private:
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||
|
||||
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
|
||||
@@ -122,7 +123,7 @@ private:
|
||||
QScopedPointer<SettingsController> m_settingsController;
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
QScopedPointer<ApiController> m_apiController;
|
||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- Enable when VPN-per-app mode will be implemented -->
|
||||
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".AmneziaApplication"
|
||||
|
||||
@@ -64,8 +64,9 @@ class Awg : Wireguard() {
|
||||
val configDataJson = config.getJSONObject("awg_config_data")
|
||||
val configData = parseConfigData(configDataJson.getString("config"))
|
||||
return AwgConfig.build {
|
||||
configWireguard(configData)
|
||||
configWireguard(configData, configDataJson)
|
||||
configSplitTunneling(config)
|
||||
configAppSplitTunneling(config)
|
||||
configData["Jc"]?.let { setJc(it.toInt()) }
|
||||
configData["Jmin"]?.let { setJmin(it.toInt()) }
|
||||
configData["Jmax"]?.let { setJmax(it.toInt()) }
|
||||
|
||||
@@ -79,6 +79,7 @@ open class OpenVpn : Protocol() {
|
||||
}
|
||||
configPluggableTransport(configBuilder, config)
|
||||
configBuilder.configSplitTunneling(config)
|
||||
configBuilder.configAppSplitTunneling(config)
|
||||
|
||||
scope.launch {
|
||||
val status = client.connect()
|
||||
|
||||
@@ -64,6 +64,22 @@ abstract class Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
protected fun ProtocolConfig.Builder.configAppSplitTunneling(config: JSONObject) {
|
||||
val splitTunnelType = config.optInt("appSplitTunnelType")
|
||||
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
|
||||
val splitTunnelApps = config.getJSONArray("splitTunnelApps")
|
||||
val appHandlerFunc = when (splitTunnelType) {
|
||||
SPLIT_TUNNEL_INCLUDE -> ::includeApplication
|
||||
SPLIT_TUNNEL_EXCLUDE -> ::excludeApplication
|
||||
|
||||
else -> throw BadConfigException("Unexpected value of the 'appSplitTunnelType' parameter: $splitTunnelType")
|
||||
}
|
||||
|
||||
for (i in 0 until splitTunnelApps.length()) {
|
||||
appHandlerFunc(splitTunnelApps.getString(i))
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun buildVpnInterface(config: ProtocolConfig, vpnBuilder: Builder) {
|
||||
vpnBuilder.setSession(VPN_SESSION_NAME)
|
||||
|
||||
@@ -101,8 +117,13 @@ abstract class Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
for (app in config.includedApplications) {
|
||||
Log.d(TAG, "addAllowedApplication")
|
||||
vpnBuilder.addAllowedApplication(app)
|
||||
}
|
||||
|
||||
for (app in config.excludedApplications) {
|
||||
Log.d(TAG, "addDisallowedApplication: $app")
|
||||
Log.d(TAG, "addDisallowedApplication")
|
||||
vpnBuilder.addDisallowedApplication(app)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ open class ProtocolConfig protected constructor(
|
||||
val excludedRoutes: Set<InetNetwork>,
|
||||
val includedAddresses: Set<InetNetwork>,
|
||||
val excludedAddresses: Set<InetNetwork>,
|
||||
val includedApplications: Set<String>,
|
||||
val excludedApplications: Set<String>,
|
||||
val httpProxy: ProxyInfo?,
|
||||
val allowAllAF: Boolean,
|
||||
@@ -31,6 +32,7 @@ open class ProtocolConfig protected constructor(
|
||||
builder.excludedRoutes,
|
||||
builder.includedAddresses,
|
||||
builder.excludedAddresses,
|
||||
builder.includedApplications,
|
||||
builder.excludedApplications,
|
||||
builder.httpProxy,
|
||||
builder.allowAllAF,
|
||||
@@ -45,6 +47,7 @@ open class ProtocolConfig protected constructor(
|
||||
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val includedApplications: MutableSet<String> = hashSetOf()
|
||||
internal val excludedApplications: MutableSet<String> = hashSetOf()
|
||||
|
||||
internal var searchDomain: String? = null
|
||||
@@ -88,6 +91,9 @@ open class ProtocolConfig protected constructor(
|
||||
fun excludeAddress(addr: InetNetwork) = apply { this.excludedAddresses += addr }
|
||||
fun excludeAddresses(addresses: Collection<InetNetwork>) = apply { this.excludedAddresses += addresses }
|
||||
|
||||
fun includeApplication(application: String) = apply { this.includedApplications += application }
|
||||
fun includeApplications(applications: Collection<String>) = apply { this.includedApplications += applications }
|
||||
|
||||
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
|
||||
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
|
||||
|
||||
|
||||
@@ -2,4 +2,11 @@
|
||||
<resources>
|
||||
<string name="connecting">Подключение</string>
|
||||
<string name="disconnecting">Отключение</string>
|
||||
<string name="cancel">Отмена</string>
|
||||
<string name="ok">ОК</string>
|
||||
<string name="vpnGranted">VPN-подключение разрешено</string>
|
||||
<string name="vpnDenied">VPN-подключение запрещено</string>
|
||||
<string name="vpnSetupFailed">Ошибка настройки VPN</string>
|
||||
<string name="vpnSetupFailedMessage">Чтобы подключиться к AmneziaVPN необходимо:\n\n- Разрешить приложению подключаться к сети VPN\n- Отключить функцию \"Постоянная VPN\" для всех остальных VPN-приложений в системных настройках VPN</string>
|
||||
<string name="openVpnSettings">Открыть настройки VPN</string>
|
||||
</resources>
|
||||
@@ -2,4 +2,11 @@
|
||||
<resources>
|
||||
<string name="connecting">Connecting</string>
|
||||
<string name="disconnecting">Disconnecting</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="vpnGranted">VPN permission granted</string>
|
||||
<string name="vpnDenied">VPN permission denied</string>
|
||||
<string name="vpnSetupFailed">VPN setup error</string>
|
||||
<string name="vpnSetupFailedMessage">To connect to AmneziaVPN, please do the following:\n\n- Allow the app to set up a VPN connection\n- Disable Always-on VPN for any other VPN app in the VPN system settings</string>
|
||||
<string name="openVpnSettings">Open VPN settings</string>
|
||||
</resources>
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.Intent.EXTRA_MIME_TYPES
|
||||
import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.net.VpnService
|
||||
import android.os.Bundle
|
||||
@@ -14,6 +16,7 @@ import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.os.Messenger
|
||||
import android.provider.Settings
|
||||
import android.view.WindowManager.LayoutParams
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
@@ -22,12 +25,15 @@ import androidx.core.content.ContextCompat
|
||||
import java.io.IOException
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
import AppListProvider
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.amnezia.vpn.protocol.getStatistics
|
||||
import org.amnezia.vpn.protocol.getStatus
|
||||
import org.amnezia.vpn.qt.QtAndroidController
|
||||
@@ -216,13 +222,13 @@ class AmneziaActivity : QtActivity() {
|
||||
when (resultCode) {
|
||||
RESULT_OK -> {
|
||||
Log.d(TAG, "Vpn permission granted")
|
||||
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this, resources.getText(R.string.vpnGranted), Toast.LENGTH_LONG).show()
|
||||
checkVpnPermissionCallbacks?.run { onSuccess() }
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.w(TAG, "Vpn permission denied, resultCode: $resultCode")
|
||||
Toast.makeText(this, "Vpn permission denied", Toast.LENGTH_LONG).show()
|
||||
showOnVpnPermissionRejectDialog()
|
||||
checkVpnPermissionCallbacks?.run { onFail() }
|
||||
}
|
||||
}
|
||||
@@ -280,6 +286,17 @@ class AmneziaActivity : QtActivity() {
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
private fun showOnVpnPermissionRejectDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.vpnSetupFailed)
|
||||
.setMessage(R.string.vpnSetupFailedMessage)
|
||||
.setNegativeButton(R.string.ok) { _, _ -> }
|
||||
.setPositiveButton(R.string.openVpnSettings) { _, _ ->
|
||||
startActivity(Intent(Settings.ACTION_VPN_SETTINGS))
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun startVpn(vpnConfig: String) {
|
||||
if (isServiceConnected) {
|
||||
@@ -389,25 +406,29 @@ class AmneziaActivity : QtActivity() {
|
||||
Log.v(TAG, "Open file with filter: $filter")
|
||||
|
||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||
val extensionRegex = "\\*\\.[a-z .]+".toRegex(IGNORE_CASE)
|
||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||
val mime = MimeTypeMap.getSingleton()
|
||||
extensionRegex.findAll(filter).map {
|
||||
mime.getMimeTypeFromExtension(it.value.drop(2))
|
||||
}.filterNotNull().toSet()
|
||||
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
||||
}.toSet()
|
||||
} else emptySet()
|
||||
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||
when (mimeTypes.size) {
|
||||
1 -> type = mimeTypes.first()
|
||||
if ("*/*" in mimeTypes) {
|
||||
type = "*/*"
|
||||
} else {
|
||||
when (mimeTypes.size) {
|
||||
1 -> type = mimeTypes.first()
|
||||
|
||||
in 2..Int.MAX_VALUE -> {
|
||||
type = "*/*"
|
||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||
in 2..Int.MAX_VALUE -> {
|
||||
type = "*/*"
|
||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||
}
|
||||
|
||||
else -> type = "*/*"
|
||||
}
|
||||
|
||||
else -> type = "*/*"
|
||||
}
|
||||
}.also {
|
||||
startActivityForResult(it, OPEN_FILE_ACTION_CODE)
|
||||
@@ -432,7 +453,7 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
@Suppress("unused")
|
||||
fun setSaveLogs(enabled: Boolean) {
|
||||
Log.d(TAG, "Set save logs: $enabled")
|
||||
Log.v(TAG, "Set save logs: $enabled")
|
||||
mainScope.launch {
|
||||
Log.saveLogs = enabled
|
||||
vpnServiceMessenger.send {
|
||||
@@ -452,7 +473,9 @@ class AmneziaActivity : QtActivity() {
|
||||
@Suppress("unused")
|
||||
fun clearLogs() {
|
||||
Log.v(TAG, "Clear logs")
|
||||
Log.clearLogs()
|
||||
mainScope.launch {
|
||||
Log.clearLogs()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -463,4 +486,32 @@ class AmneziaActivity : QtActivity() {
|
||||
window.setFlags(flag, LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun minimizeApp() {
|
||||
Log.v(TAG, "Minimize application")
|
||||
mainScope.launch {
|
||||
moveTaskToBack(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun getAppList(): String {
|
||||
Log.v(TAG, "Get app list")
|
||||
var appList = ""
|
||||
runBlocking {
|
||||
mainScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
appList = AppListProvider.getAppList(packageManager, packageName)
|
||||
}
|
||||
}.join()
|
||||
}
|
||||
return appList
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun getAppIcon(packageName: String, width: Int, height: Int): Bitmap {
|
||||
Log.v(TAG, "Get app icon")
|
||||
return AppListProvider.getAppIcon(packageManager, packageName, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,11 +215,9 @@ class AmneziaVpnService : VpnService() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val isAlwaysOnCompat =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) isAlwaysOn
|
||||
else intent?.component?.packageName != packageName
|
||||
val isAlwaysOn = intent != null && intent.action == SERVICE_INTERFACE
|
||||
|
||||
if (isAlwaysOnCompat) {
|
||||
if (isAlwaysOn) {
|
||||
Log.d(TAG, "Start service via Always-on")
|
||||
connect()
|
||||
} else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import android.Manifest.permission.INTERNET
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Bitmap.Config.ARGB_8888
|
||||
import androidx.core.graphics.drawable.toBitmapOrNull
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
private const val TAG = "AppListProvider"
|
||||
|
||||
object AppListProvider {
|
||||
fun getAppList(pm: PackageManager, selfPackageName: String): String {
|
||||
val jsonArray = JSONArray()
|
||||
pm.getPackagesHoldingPermissions(arrayOf(INTERNET), 0)
|
||||
.filter { it.packageName != selfPackageName }
|
||||
.map { App(it, pm) }
|
||||
.sortedWith(App::compareTo)
|
||||
.map(App::toJson)
|
||||
.forEach(jsonArray::put)
|
||||
return jsonArray.toString()
|
||||
}
|
||||
|
||||
fun getAppIcon(pm: PackageManager, packageName: String, width: Int, height: Int): Bitmap {
|
||||
val icon = try {
|
||||
pm.getApplicationIcon(packageName)
|
||||
} catch (e: NameNotFoundException) {
|
||||
Log.e(TAG, "Package $packageName was not found: $e")
|
||||
pm.defaultActivityIcon
|
||||
}
|
||||
val w: Int = if (width > 0) width else icon.intrinsicWidth
|
||||
val h: Int = if (height > 0) height else icon.intrinsicHeight
|
||||
return icon.toBitmapOrNull(w, h, ARGB_8888)
|
||||
?: Bitmap.createBitmap(w, h, ARGB_8888)
|
||||
}
|
||||
}
|
||||
|
||||
private class App(pi: PackageInfo, pm: PackageManager, ai: ApplicationInfo = pi.applicationInfo) : Comparable<App> {
|
||||
val name: String?
|
||||
val packageName: String = pi.packageName
|
||||
val icon: Boolean = ai.icon != 0
|
||||
val isLaunchable: Boolean = pm.getLaunchIntentForPackage(packageName) != null
|
||||
|
||||
init {
|
||||
val name = ai.loadLabel(pm).toString()
|
||||
this.name = if (name != packageName) name else null
|
||||
}
|
||||
|
||||
override fun compareTo(other: App): Int {
|
||||
val r = other.isLaunchable.compareTo(isLaunchable)
|
||||
if (r != 0) return r
|
||||
if (name != other.name) {
|
||||
return when {
|
||||
name == null -> 1
|
||||
other.name == null -> -1
|
||||
else -> String.CASE_INSENSITIVE_ORDER.compare(name, other.name)
|
||||
}
|
||||
}
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(packageName, other.packageName)
|
||||
}
|
||||
|
||||
fun toJson(): JSONObject {
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("package", packageName)
|
||||
jsonObject.put("name", name)
|
||||
jsonObject.put("icon", icon)
|
||||
jsonObject.put("launchable", isLaunchable)
|
||||
return jsonObject
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.KeyguardManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.net.VpnService
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.result.ActivityResult
|
||||
@@ -52,19 +56,43 @@ class VpnRequestActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
private fun checkRequestResult(result: ActivityResult) {
|
||||
when (result.resultCode) {
|
||||
RESULT_OK -> onPermissionGranted()
|
||||
else -> Toast.makeText(this, "Vpn permission denied", Toast.LENGTH_LONG).show()
|
||||
when (val resultCode = result.resultCode) {
|
||||
RESULT_OK -> {
|
||||
onPermissionGranted()
|
||||
finish()
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.w(TAG, "Vpn permission denied, resultCode: $resultCode")
|
||||
showOnVpnPermissionRejectDialog()
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun onPermissionGranted() {
|
||||
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this, resources.getString(R.string.vpnGranted), Toast.LENGTH_LONG).show()
|
||||
Intent(applicationContext, AmneziaVpnService::class.java).apply {
|
||||
putExtra(AFTER_PERMISSION_CHECK, true)
|
||||
}.also {
|
||||
ContextCompat.startForegroundService(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOnVpnPermissionRejectDialog() {
|
||||
AlertDialog.Builder(this, getDialogTheme())
|
||||
.setTitle(R.string.vpnSetupFailed)
|
||||
.setMessage(R.string.vpnSetupFailedMessage)
|
||||
.setNegativeButton(R.string.ok) { _, _ -> }
|
||||
.setPositiveButton(R.string.openVpnSettings) { _, _ ->
|
||||
startActivity(Intent(Settings.ACTION_VPN_SETTINGS))
|
||||
}
|
||||
.setOnDismissListener { finish() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun getDialogTheme(): Int =
|
||||
if (resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES)
|
||||
android.R.style.Theme_DeviceDefault_Dialog_Alert
|
||||
else
|
||||
android.R.style.Theme_DeviceDefault_Light_Dialog_Alert
|
||||
}
|
||||
|
||||
@@ -109,9 +109,11 @@ object Log {
|
||||
"${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}"
|
||||
|
||||
fun clearLogs() {
|
||||
withLock {
|
||||
logFile.delete()
|
||||
rotateLogFile.delete()
|
||||
if (logDir.exists()) {
|
||||
withLock {
|
||||
logFile.delete()
|
||||
rotateLogFile.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.amnezia.vpn.protocol.wireguard
|
||||
|
||||
object GoBackend {
|
||||
external fun wgGetConfig(handle: Int): String?
|
||||
external fun wgGetSocketV4(handle: Int): Int
|
||||
external fun wgGetSocketV6(handle: Int): Int
|
||||
external fun wgTurnOff(handle: Int)
|
||||
external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
|
||||
external fun wgVersion(): String
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.amnezia.awg
|
||||
|
||||
object GoBackend {
|
||||
external fun awgGetConfig(handle: Int): String?
|
||||
external fun awgGetSocketV4(handle: Int): Int
|
||||
external fun awgGetSocketV6(handle: Int): Int
|
||||
external fun awgTurnOff(handle: Int)
|
||||
external fun awgTurnOn(ifName: String, tunFd: Int, settings: String): Int
|
||||
external fun awgVersion(): String
|
||||
}
|
||||
+18
-9
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import java.util.TreeMap
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.amnezia.awg.GoBackend
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||
@@ -61,7 +62,7 @@ open class Wireguard : Protocol() {
|
||||
override val statistics: Statistics
|
||||
get() {
|
||||
if (tunnelHandle == -1) return Statistics.EMPTY_STATISTICS
|
||||
val config = GoBackend.wgGetConfig(tunnelHandle) ?: return Statistics.EMPTY_STATISTICS
|
||||
val config = GoBackend.awgGetConfig(tunnelHandle) ?: return Statistics.EMPTY_STATISTICS
|
||||
return Statistics.build {
|
||||
var optsCount = 0
|
||||
config.splitToSequence("\n").forEach { line ->
|
||||
@@ -92,12 +93,13 @@ open class Wireguard : Protocol() {
|
||||
val configDataJson = config.getJSONObject("wireguard_config_data")
|
||||
val configData = parseConfigData(configDataJson.getString("config"))
|
||||
return WireguardConfig.build {
|
||||
configWireguard(configData)
|
||||
configWireguard(configData, configDataJson)
|
||||
configSplitTunneling(config)
|
||||
configAppSplitTunneling(config)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun WireguardConfig.Builder.configWireguard(configData: Map<String, String>) {
|
||||
protected fun WireguardConfig.Builder.configWireguard(configData: Map<String, String>, configDataJson: JSONObject) {
|
||||
configData["Address"]?.split(",")?.map { address ->
|
||||
InetNetwork.parse(address.trim())
|
||||
}?.forEach(::addAddress)
|
||||
@@ -118,7 +120,14 @@ open class Wireguard : Protocol() {
|
||||
if (routes.any { it !in defRoutes }) disableSplitTunneling()
|
||||
addRoutes(routes)
|
||||
|
||||
configData["MTU"]?.let { setMtu(it.toInt()) }
|
||||
configDataJson.optString("mtu").let { mtu ->
|
||||
if (mtu.isNotEmpty()) {
|
||||
setMtu(mtu.toInt())
|
||||
} else {
|
||||
configData["MTU"]?.let { setMtu(it.toInt()) }
|
||||
}
|
||||
}
|
||||
|
||||
configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) }
|
||||
configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) }
|
||||
configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) }
|
||||
@@ -149,8 +158,8 @@ open class Wireguard : Protocol() {
|
||||
if (tunFd == null) {
|
||||
throw VpnStartException("Create VPN interface: permission not granted or revoked")
|
||||
}
|
||||
Log.v(TAG, "Wg-go backend ${GoBackend.wgVersion()}")
|
||||
tunnelHandle = GoBackend.wgTurnOn(ifName, tunFd.detachFd(), config.toWgUserspaceString())
|
||||
Log.i(TAG, "awg-go backend ${GoBackend.awgVersion()}")
|
||||
tunnelHandle = GoBackend.awgTurnOn(ifName, tunFd.detachFd(), config.toWgUserspaceString())
|
||||
}
|
||||
|
||||
if (tunnelHandle < 0) {
|
||||
@@ -158,8 +167,8 @@ open class Wireguard : Protocol() {
|
||||
throw VpnStartException("Wireguard tunnel creation error")
|
||||
}
|
||||
|
||||
if (!protect(GoBackend.wgGetSocketV4(tunnelHandle)) || !protect(GoBackend.wgGetSocketV6(tunnelHandle))) {
|
||||
GoBackend.wgTurnOff(tunnelHandle)
|
||||
if (!protect(GoBackend.awgGetSocketV4(tunnelHandle)) || !protect(GoBackend.awgGetSocketV6(tunnelHandle))) {
|
||||
GoBackend.awgTurnOff(tunnelHandle)
|
||||
tunnelHandle = -1
|
||||
throw VpnStartException("Protect VPN interface: permission not granted or revoked")
|
||||
}
|
||||
@@ -172,7 +181,7 @@ open class Wireguard : Protocol() {
|
||||
}
|
||||
val handleToClose = tunnelHandle
|
||||
tunnelHandle = -1
|
||||
GoBackend.wgTurnOff(handleToClose)
|
||||
GoBackend.awgTurnOff(handleToClose)
|
||||
state.value = DISCONNECTED
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ set(QT_ANDROID_MULTI_ABI_FORWARD_VARS "QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL;CMAKE
|
||||
|
||||
# We need to include qtprivate api's
|
||||
# As QAndroidBinder is not yet implemented with a public api
|
||||
set(LIBS ${LIBS} Qt6::CorePrivate)
|
||||
set(LIBS ${LIBS} Qt6::CorePrivate -ljnigraphics)
|
||||
|
||||
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
|
||||
|
||||
@@ -30,6 +30,7 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
@@ -38,20 +39,18 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
|
||||
)
|
||||
|
||||
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-go.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-quick.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libredsocks.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libsslocal.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libtun2socks.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libck-ovpn-plugin.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/librsapss.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libcrypto_3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libssl_3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
|
||||
)
|
||||
endforeach()
|
||||
|
||||
@@ -108,16 +108,19 @@ target_sources(${PROJECT} PRIVATE
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
|
||||
@@ -3,17 +3,15 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/controllers/serverController.h"
|
||||
|
||||
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: WireguardConfigurator(settings, true, parent)
|
||||
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: WireguardConfigurator(settings, serverController, true, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
|
||||
|
||||
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
||||
@@ -41,6 +39,8 @@ QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, Dock
|
||||
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);
|
||||
jsonConfig[config_key::mtu] =
|
||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||
|
||||
return QJsonDocument(jsonConfig).toJson();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ class AwgConfigurator : public WireguardConfigurator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode);
|
||||
};
|
||||
|
||||
#endif // AWGCONFIGURATOR_H
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
#include "cloak_configurator.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
|
||||
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
ServerController serverController(m_settings);
|
||||
|
||||
QString cloakPublicKey = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::cloak::ckPublicKeyPath, &e);
|
||||
QString cloakPublicKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
|
||||
cloakPublicKey.replace("\n", "");
|
||||
|
||||
QString cloakBypassUid = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::cloak::ckBypassUidKeyPath, &e);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
QString cloakBypassUid =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckBypassUidKeyPath, errorCode);
|
||||
cloakBypassUid.replace("\n", "");
|
||||
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -45,9 +44,8 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
|
||||
config.insert("RemoteHost", credentials.hostName);
|
||||
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
|
||||
|
||||
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
// qDebug().noquote() << textCfg;
|
||||
return textCfg;
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class CloakConfigurator : ConfiguratorBase
|
||||
class CloakConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode);
|
||||
};
|
||||
|
||||
#endif // CLOAK_CONFIGURATOR_H
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
#include "configurator_base.h"
|
||||
|
||||
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_settings(settings)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3,19 +3,31 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Settings;
|
||||
|
||||
#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, QObject *parent = nullptr);
|
||||
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
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
#include <QUuid>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "utilities.h"
|
||||
|
||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials,
|
||||
DockerContainer container, ErrorCode *errorCode)
|
||||
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
Ikev2Configurator::ConnectionData connData;
|
||||
connData.host = credentials.hostName;
|
||||
@@ -39,18 +39,14 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
|
||||
.arg(connData.clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
ErrorCode e = serverController.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);
|
||||
e = serverController.runContainerScript(credentials, container, scriptExportCert);
|
||||
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_serverController->runContainerScript(credentials, container, scriptExportCert);
|
||||
|
||||
connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, &e);
|
||||
connData.caCert =
|
||||
serverController.getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e);
|
||||
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();
|
||||
@@ -58,13 +54,13 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
Q_UNUSED(containerConfig)
|
||||
|
||||
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
|
||||
if (errorCode && *errorCode) {
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include "configurator_base.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class Ikev2Configurator : ConfiguratorBase
|
||||
class Ikev2Configurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData {
|
||||
QByteArray clientCert; // p12 client cert
|
||||
@@ -21,15 +21,15 @@ public:
|
||||
QString host; // host ip
|
||||
};
|
||||
|
||||
QString genIkev2Config(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||
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 = nullptr);
|
||||
DockerContainer container, ErrorCode errorCode);
|
||||
};
|
||||
|
||||
#endif // IKEV2_CONFIGURATOR_H
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#endif
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
@@ -24,74 +24,61 @@
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, 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,
|
||||
ErrorCode *errorCode)
|
||||
DockerContainer container, ErrorCode errorCode)
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
|
||||
connData.host = credentials.hostName;
|
||||
|
||||
if (connData.privKey.isEmpty() || connData.request.isEmpty()) {
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::OpenSslFailed;
|
||||
errorCode = ErrorCode::OpenSslFailed;
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath).arg(connData.clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
ErrorCode e = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
||||
if (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
e = signCert(container, credentials, connData.clientId);
|
||||
if (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
errorCode = signCert(container, credentials, connData.clientId);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.caCert = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::openvpn::caCertPath, &e);
|
||||
connData.clientCert = serverController.getTextFileFromContainer(
|
||||
container, credentials,
|
||||
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
|
||||
connData.caCert =
|
||||
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 (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.taKey = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::openvpn::taKeyPath, &e);
|
||||
connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
|
||||
|
||||
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::SshScpFailureError;
|
||||
errorCode = ErrorCode::SshScpFailureError;
|
||||
}
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode)
|
||||
{
|
||||
ServerController serverController(m_settings);
|
||||
QString config =
|
||||
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
|
||||
if (errorCode && *errorCode) {
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -113,34 +100,35 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
||||
QJsonObject jConfig;
|
||||
jConfig[config_key::config] = config;
|
||||
|
||||
clientId = connData.clientId;
|
||||
jConfig[config_key::clientId] = connData.clientId;
|
||||
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig, const int serverIndex)
|
||||
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
{
|
||||
QJsonObject json = QJsonDocument::fromJson(jsonConfig.toUtf8()).object();
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
QString config = json[config_key::config].toString();
|
||||
|
||||
if (!m_settings->server(serverIndex).value(config_key::configVersion).toInt()) {
|
||||
if (!isApiConfig) {
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
|
||||
if (m_settings->routeMode() == Settings::VpnAllSites) {
|
||||
|
||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("block-ipv6\n");
|
||||
}
|
||||
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
|
||||
// no redirect-gateway
|
||||
}
|
||||
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#ifndef Q_OS_ANDROID
|
||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#ifndef Q_OS_ANDROID
|
||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||
#endif
|
||||
#endif
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("block-ipv6\n");
|
||||
@@ -164,9 +152,12 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig,
|
||||
return QJsonDocument(json).toJson();
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig)
|
||||
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
{
|
||||
QJsonObject json = QJsonDocument::fromJson(jsonConfig.toUtf8()).object();
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
QString config = json[config_key::config].toString();
|
||||
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
@@ -198,12 +189,10 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerC
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
QStringList scriptList { script_import, script_sign };
|
||||
QString script = serverController.replaceVars(scriptList.join("\n"),
|
||||
serverController.genVarsForScript(credentials, container));
|
||||
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
|
||||
|
||||
return serverController.runScript(credentials, script);
|
||||
return m_serverController->runScript(credentials, script);
|
||||
}
|
||||
|
||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
@@ -237,8 +226,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
|
||||
X509_NAME_add_entry_by_txt(x509_name, "C", MBSTRING_ASC, (unsigned char *)"ORG", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "O", MBSTRING_ASC, (unsigned char *)"", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC,
|
||||
reinterpret_cast<unsigned char const *>(clientIdUtf8.data()), clientIdUtf8.size(), -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC, reinterpret_cast<unsigned char const *>(clientIdUtf8.data()),
|
||||
clientIdUtf8.size(), -1, 0);
|
||||
|
||||
// 4. set public key of x509 req
|
||||
ret = X509_REQ_set_pubkey(x509_req, pKey);
|
||||
|
||||
@@ -7,37 +7,37 @@
|
||||
#include "configurator_base.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class OpenVpnConfigurator : ConfiguratorBase
|
||||
class OpenVpnConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData {
|
||||
struct ConnectionData
|
||||
{
|
||||
QString clientId;
|
||||
QString request; // certificate request
|
||||
QString privKey; // client private key
|
||||
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 caCert; // server certificate
|
||||
QString taKey; // tls-auth key
|
||||
QString host; // host ip
|
||||
};
|
||||
|
||||
QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode);
|
||||
|
||||
QString processConfigWithLocalSettings(QString jsonConfig, const int serverIndex);
|
||||
QString processConfigWithExportSettings(QString jsonConfig);
|
||||
|
||||
ErrorCode signCert(DockerContainer container,
|
||||
const ServerCredentials &credentials, QString clientId);
|
||||
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 = nullptr);
|
||||
|
||||
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
ErrorCode errorCode);
|
||||
ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId);
|
||||
};
|
||||
|
||||
#endif // OPENVPN_CONFIGURATOR_H
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
#include "shadowsocks_configurator.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
|
||||
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode)
|
||||
{
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
ServerController serverController(m_settings);
|
||||
|
||||
QString ssKey = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::shadowsocks::ssKeyPath, &e);
|
||||
QString ssKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
|
||||
ssKey.replace("\n", "");
|
||||
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -36,10 +32,9 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c
|
||||
config.insert("timeout", 60);
|
||||
config.insert("method", "$SHADOWSOCKS_CIPHER");
|
||||
|
||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
//qDebug().noquote() << textCfg;
|
||||
// qDebug().noquote() << textCfg;
|
||||
return textCfg;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
#include "configurator_base.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class ShadowSocksConfigurator : ConfiguratorBase
|
||||
class ShadowSocksConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode);
|
||||
};
|
||||
|
||||
#endif // SHADOWSOCKS_CONFIGURATOR_H
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "core/server_defs.h"
|
||||
#include "utilities.h"
|
||||
|
||||
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -82,8 +82,7 @@ void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||
// 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));
|
||||
p->setNativeArguments(QString("%1@%2 -pw %3").arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
|
||||
}
|
||||
#else
|
||||
p->setProgram("/bin/bash");
|
||||
|
||||
@@ -11,7 +11,7 @@ class SshConfigurator : ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QProcessEnvironment prepareEnv();
|
||||
QString convertOpenSShKey(const QString &key);
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "vpn_configurator.h"
|
||||
#include "cloak_configurator.h"
|
||||
#include "ikev2_configurator.h"
|
||||
#include "openvpn_configurator.h"
|
||||
#include "shadowsocks_configurator.h"
|
||||
#include "ssh_configurator.h"
|
||||
#include "wireguard_configurator.h"
|
||||
#include "awg_configurator.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
{
|
||||
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, this));
|
||||
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, this));
|
||||
cloakConfigurator = std::shared_ptr<CloakConfigurator>(new CloakConfigurator(settings, this));
|
||||
wireguardConfigurator = std::shared_ptr<WireguardConfigurator>(new WireguardConfigurator(settings, false, this));
|
||||
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, this));
|
||||
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
|
||||
awgConfigurator = std::shared_ptr<AwgConfigurator>(new AwgConfigurator(settings, this));
|
||||
}
|
||||
|
||||
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, Proto proto, QString &clientId, ErrorCode *errorCode)
|
||||
{
|
||||
switch (proto) {
|
||||
case Proto::OpenVpn:
|
||||
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||
|
||||
case Proto::ShadowSocks:
|
||||
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
|
||||
|
||||
case Proto::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
|
||||
|
||||
case Proto::WireGuard:
|
||||
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||
|
||||
case Proto::Awg:
|
||||
return awgConfigurator->genAwgConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||
|
||||
case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
|
||||
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
|
||||
{
|
||||
QPair<QString, QString> dns;
|
||||
|
||||
bool useAmneziaDns = m_settings->useAmneziaDns();
|
||||
const QJsonObject &server = m_settings->server(serverIndex);
|
||||
|
||||
dns.first = server.value(config_key::dns1).toString();
|
||||
dns.second = server.value(config_key::dns2).toString();
|
||||
|
||||
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
|
||||
if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) {
|
||||
dns.first = protocols::dns::amneziaDnsIp;
|
||||
} else
|
||||
dns.first = m_settings->primaryDns();
|
||||
}
|
||||
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
|
||||
dns.second = m_settings->secondaryDns();
|
||||
}
|
||||
|
||||
qDebug() << "VpnConfigurator::getDnsForConfig" << dns.first << dns.second;
|
||||
return dns;
|
||||
}
|
||||
|
||||
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||
QString &config)
|
||||
{
|
||||
auto dns = getDnsForConfig(serverIndex);
|
||||
|
||||
config.replace("$PRIMARY_DNS", dns.first);
|
||||
config.replace("$SECONDARY_DNS", dns.second);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||
QString &config)
|
||||
{
|
||||
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||
|
||||
if (proto == Proto::OpenVpn) {
|
||||
config = openVpnConfigurator->processConfigWithLocalSettings(config, serverIndex);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||
QString &config)
|
||||
{
|
||||
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||
|
||||
if (proto == Proto::OpenVpn) {
|
||||
config = openVpnConfigurator->processConfigWithExportSettings(config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
void VpnConfigurator::updateContainerConfigAfterInstallation(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);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef VPN_CONFIGURATOR_H
|
||||
#define VPN_CONFIGURATOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class OpenVpnConfigurator;
|
||||
class ShadowSocksConfigurator;
|
||||
class CloakConfigurator;
|
||||
class WireguardConfigurator;
|
||||
class Ikev2Configurator;
|
||||
class SshConfigurator;
|
||||
class AwgConfigurator;
|
||||
|
||||
// Retrieve connection settings from server
|
||||
class VpnConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, Proto proto, QString &clientId,
|
||||
ErrorCode *errorCode = nullptr);
|
||||
|
||||
QPair<QString, QString> getDnsForConfig(int serverIndex);
|
||||
QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||
|
||||
QString &processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||
QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||
|
||||
// workaround for containers which is not support normal configuration
|
||||
void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
|
||||
const QString &stdOut);
|
||||
|
||||
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
|
||||
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
|
||||
std::shared_ptr<CloakConfigurator> cloakConfigurator;
|
||||
std::shared_ptr<WireguardConfigurator> wireguardConfigurator;
|
||||
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
|
||||
std::shared_ptr<SshConfigurator> sshConfigurator;
|
||||
std::shared_ptr<AwgConfigurator> awgConfigurator;
|
||||
|
||||
signals:
|
||||
void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||
ServerCredentials credentials);
|
||||
void clientModelUpdated();
|
||||
};
|
||||
|
||||
#endif // VPN_CONFIGURATOR_H
|
||||
@@ -13,23 +13,20 @@
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent), m_isAwg(isAwg)
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||
{
|
||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath
|
||||
: amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath
|
||||
: amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath
|
||||
: amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template
|
||||
: ProtocolScriptType::wireguard_template;
|
||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||
|
||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
|
||||
@@ -68,22 +65,17 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container,
|
||||
const QJsonObject &containerConfig,
|
||||
ErrorCode *errorCode)
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode)
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
|
||||
|
||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::InternalError;
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return connData;
|
||||
}
|
||||
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
ServerController serverController(m_settings);
|
||||
|
||||
// Get list of already created clients (only IP addresses)
|
||||
QString nextIpNumber;
|
||||
{
|
||||
@@ -94,9 +86,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
e = serverController.runContainerScript(credentials, container, script, cbReadStdOut);
|
||||
if (errorCode && e) {
|
||||
*errorCode = e;
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
@@ -110,21 +101,18 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
} else {
|
||||
int next = ips.last().split(".").last().toInt() + 1;
|
||||
if (next > 254) {
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::AddressPoolError;
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
nextIpNumber = QString::number(next);
|
||||
}
|
||||
}
|
||||
|
||||
QString subnetIp =
|
||||
containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
{
|
||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||
if (l.isEmpty()) {
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::AddressPoolError;
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
l.removeLast();
|
||||
@@ -134,20 +122,16 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
}
|
||||
|
||||
// Get keys
|
||||
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, &e);
|
||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey.replace("\n", "");
|
||||
if (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.pskKey = serverController.getTextFileFromContainer(container, credentials, m_serverPskKeyPath, &e);
|
||||
connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
|
||||
connData.pskKey.replace("\n", "");
|
||||
|
||||
if (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
@@ -158,34 +142,30 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
"AllowedIPs = %3/32\n\n")
|
||||
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
|
||||
|
||||
e = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath,
|
||||
libssh::ScpOverwriteMode::ScpAppendToExisting);
|
||||
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath,
|
||||
libssh::ScpOverwriteMode::ScpAppendToExisting);
|
||||
|
||||
if (e) {
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||
.arg(m_serverConfigPath);
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
||||
|
||||
e = serverController.runScript(
|
||||
credentials, serverController.replaceVars(script, serverController.genVarsForScript(credentials, container)));
|
||||
errorCode = m_serverController->runScript(
|
||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode)
|
||||
{
|
||||
ServerController serverController(m_settings);
|
||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||
QString config = serverController.replaceVars(
|
||||
scriptData, serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
QString config =
|
||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode && *errorCode) {
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -194,6 +174,7 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
|
||||
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
|
||||
config.replace("$WIREGUARD_PSK", connData.pskKey);
|
||||
|
||||
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||
QJsonObject jConfig;
|
||||
jConfig[config_key::config] = config;
|
||||
|
||||
@@ -204,28 +185,25 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
|
||||
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);
|
||||
|
||||
clientId = connData.clientPubKey;
|
||||
jConfig[config_key::clientId] = connData.clientPubKey;
|
||||
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(QString config)
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
{
|
||||
// TODO replace DNS if it already set
|
||||
config.replace("$PRIMARY_DNS", m_settings->primaryDns());
|
||||
config.replace("$SECONDARY_DNS", m_settings->secondaryDns());
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
QJsonObject jConfig;
|
||||
jConfig[config_key::config] = config;
|
||||
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
return protocolConfigString;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(QString config)
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
{
|
||||
config.replace("$PRIMARY_DNS", m_settings->primaryDns());
|
||||
config.replace("$SECONDARY_DNS", m_settings->secondaryDns());
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
return config;
|
||||
return protocolConfigString;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent = nullptr);
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData
|
||||
{
|
||||
@@ -25,18 +26,18 @@ public:
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode errorCode);
|
||||
|
||||
QString processConfigWithLocalSettings(QString config);
|
||||
QString processConfigWithExportSettings(QString config);
|
||||
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:
|
||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||
|
||||
const QJsonObject &containerConfig, ErrorCode errorCode);
|
||||
|
||||
bool m_isAwg;
|
||||
QString m_serverConfigPath;
|
||||
QString m_serverPublicKeyPath;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "xray_configurator.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
|
||||
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
QString xrayPublicKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||
xrayPublicKey.replace("\n", "");
|
||||
|
||||
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
|
||||
xrayUuid.replace("\n", "");
|
||||
|
||||
QString xrayShortId =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||
xrayShortId.replace("\n", "");
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
}
|
||||
|
||||
config.replace("$XRAY_CLIENT_ID", xrayUuid);
|
||||
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#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);
|
||||
};
|
||||
|
||||
#endif // XRAY_CONFIGURATOR_H
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "containers_defs.h"
|
||||
|
||||
#include "QJsonObject"
|
||||
#include "QJsonDocument"
|
||||
|
||||
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
@@ -58,6 +61,8 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
|
||||
|
||||
case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ };
|
||||
|
||||
case DockerContainer::Xray: return { Proto::Xray };
|
||||
|
||||
case DockerContainer::Dns: return { Proto::Dns };
|
||||
|
||||
case DockerContainer::Sftp: return { Proto::Sftp };
|
||||
@@ -85,6 +90,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
|
||||
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
|
||||
{ DockerContainer::WireGuard, "WireGuard" },
|
||||
{ DockerContainer::Awg, "AmneziaWG" },
|
||||
{ DockerContainer::Xray, "XRay" },
|
||||
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
@@ -111,6 +117,9 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
|
||||
"but very resistant to blockages. "
|
||||
"Recommended for regions with high levels of censorship.") },
|
||||
{ DockerContainer::Xray,
|
||||
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
|
||||
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
|
||||
{ DockerContainer::Ipsec,
|
||||
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
|
||||
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
||||
@@ -199,6 +208,17 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
"* Minimum number of settings\n"
|
||||
"* Not recognised by DPI analysis systems, resistant to blocking\n"
|
||||
"* Works over UDP network protocol.") },
|
||||
{ DockerContainer::Xray,
|
||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
||||
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
|
||||
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
|
||||
"thus presenting an authentic TLS certificate and data. \n"
|
||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
||||
"legitimate sites without the need for specific configurations. \n"
|
||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
|
||||
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
|
||||
},
|
||||
{ DockerContainer::Ipsec,
|
||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
||||
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
||||
@@ -213,7 +233,11 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service") }
|
||||
{ DockerContainer::Sftp,
|
||||
QObject::tr("After installation, Amnezia will create a\n\n file storage on your server. "
|
||||
"You will be able to access it using\n FileZilla or other SFTP clients, "
|
||||
"as well as mount the disk on your device to access\n it directly from your device.\n\n"
|
||||
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -231,6 +255,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
|
||||
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
|
||||
case DockerContainer::WireGuard: return Proto::WireGuard;
|
||||
case DockerContainer::Awg: return Proto::Awg;
|
||||
case DockerContainer::Xray: return Proto::Xray;
|
||||
case DockerContainer::Ipsec: return Proto::Ikev2;
|
||||
|
||||
case DockerContainer::TorWebSite: return Proto::TorWebSite;
|
||||
@@ -274,7 +299,6 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
@@ -297,7 +321,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Awg: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
// case DockerContainer::Cloak: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -306,8 +330,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("Low");
|
||||
case DockerContainer::Awg: return tr("Medium or High");
|
||||
case DockerContainer::Cloak: return tr("Extreme");
|
||||
case DockerContainer::Awg: return tr("High");
|
||||
// case DockerContainer::Cloak: return tr("Extreme");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -317,8 +341,8 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
|
||||
case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
|
||||
case DockerContainer::Cloak:
|
||||
return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||
// case DockerContainer::Cloak:
|
||||
// return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -328,7 +352,7 @@ int ContainerProps::easySetupOrder(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return 3;
|
||||
case DockerContainer::Awg: return 2;
|
||||
case DockerContainer::Cloak: return 1;
|
||||
// case DockerContainer::Cloak: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -342,3 +366,13 @@ bool ContainerProps::isShareable(DockerContainer container)
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
||||
{
|
||||
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
||||
.toObject()
|
||||
.value(config_key::last_config)
|
||||
.toString();
|
||||
|
||||
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace amnezia
|
||||
Cloak,
|
||||
ShadowSocks,
|
||||
Ipsec,
|
||||
Xray,
|
||||
|
||||
// non-vpn
|
||||
TorWebSite,
|
||||
@@ -67,6 +68,8 @@ namespace amnezia
|
||||
static int easySetupOrder(amnezia::DockerContainer container);
|
||||
|
||||
static bool isShareable(amnezia::DockerContainer container);
|
||||
|
||||
static QJsonObject getProtocolConfigFromContainer(const amnezia::Proto protocol, const QJsonObject &containerConfig);
|
||||
};
|
||||
|
||||
static void declareQmlContainerEnum()
|
||||
|
||||
+20
-42
@@ -5,9 +5,7 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "configurators/openvpn_configurator.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "core/errorstrings.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -21,12 +19,11 @@ namespace
|
||||
constexpr char certificate[] = "certificate";
|
||||
constexpr char publicKey[] = "public_key";
|
||||
constexpr char protocol[] = "protocol";
|
||||
constexpr char uuid[] = "installation_uuid";
|
||||
}
|
||||
}
|
||||
|
||||
ApiController::ApiController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ContainersModel> &containersModel, QObject *parent)
|
||||
: QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel)
|
||||
ApiController::ApiController(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -67,21 +64,14 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ApiController::updateServerConfigFromApi()
|
||||
ErrorCode ApiController::updateServerConfigFromApi(const QString &installationUuid, QJsonObject &serverConfig)
|
||||
{
|
||||
QtConcurrent::run([this]() {
|
||||
if (m_isConfigUpdateStarted) {
|
||||
emit updateFinished(false);
|
||||
return;
|
||||
}
|
||||
QFutureWatcher<ErrorCode> watcher;
|
||||
|
||||
auto serverConfig = m_serversModel->getDefaultServerConfig();
|
||||
QFuture<ErrorCode> future = QtConcurrent::run([this, &serverConfig, &installationUuid]() {
|
||||
auto containerConfig = serverConfig.value(config_key::containers).toArray();
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt() && containerConfig.isEmpty()) {
|
||||
emit updateStarted();
|
||||
m_isConfigUpdateStarted = true;
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
QNetworkRequest request;
|
||||
@@ -96,7 +86,10 @@ void ApiController::updateServerConfigFromApi()
|
||||
|
||||
auto apiPayloadData = generateApiPayloadData(protocol);
|
||||
|
||||
QByteArray requestBody = QJsonDocument(fillApiPayload(protocol, apiPayloadData)).toJson();
|
||||
auto apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
|
||||
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
|
||||
|
||||
QScopedPointer<QNetworkReply> reply;
|
||||
reply.reset(manager.post(request, requestBody));
|
||||
@@ -114,9 +107,7 @@ void ApiController::updateServerConfigFromApi()
|
||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
|
||||
if (ba.isEmpty()) {
|
||||
emit errorOccurred(errorString(ApiConfigDownloadError));
|
||||
m_isConfigUpdateStarted = false;
|
||||
return;
|
||||
return ErrorCode::ApiConfigDownloadError;
|
||||
}
|
||||
|
||||
QByteArray ba_uncompressed = qUncompress(ba);
|
||||
@@ -136,35 +127,22 @@ void ApiController::updateServerConfigFromApi()
|
||||
|
||||
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig.insert(config_key::defaultContainer, defaultContainer);
|
||||
m_serversModel->editServer(serverConfig, m_serversModel->getDefaultServerIndex());
|
||||
} else {
|
||||
QString err = reply->errorString();
|
||||
qDebug() << QString::fromUtf8(reply->readAll());
|
||||
qDebug() << reply->error();
|
||||
qDebug() << err;
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
emit errorOccurred(errorString(ApiConfigDownloadError));
|
||||
m_isConfigUpdateStarted = false;
|
||||
return;
|
||||
return ErrorCode::ApiConfigDownloadError;
|
||||
}
|
||||
}
|
||||
|
||||
emit updateFinished(m_isConfigUpdateStarted);
|
||||
m_isConfigUpdateStarted = false;
|
||||
return;
|
||||
return ErrorCode::NoError;
|
||||
});
|
||||
}
|
||||
|
||||
void ApiController::clearApiConfig()
|
||||
{
|
||||
auto serverConfig = m_serversModel->getDefaultServerConfig();
|
||||
|
||||
serverConfig.remove(config_key::dns1);
|
||||
serverConfig.remove(config_key::dns2);
|
||||
serverConfig.remove(config_key::containers);
|
||||
serverConfig.remove(config_key::hostName);
|
||||
|
||||
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
|
||||
|
||||
m_serversModel->editServer(serverConfig, m_serversModel->getDefaultServerIndex());
|
||||
|
||||
QEventLoop wait;
|
||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
|
||||
watcher.setFuture(future);
|
||||
wait.exec();
|
||||
|
||||
return watcher.result();
|
||||
}
|
||||
@@ -4,26 +4,16 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "configurators/openvpn_configurator.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ApiController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ContainersModel> &containersModel, QObject *parent = nullptr);
|
||||
explicit ApiController(QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void updateServerConfigFromApi();
|
||||
|
||||
void clearApiConfig();
|
||||
|
||||
signals:
|
||||
void updateStarted();
|
||||
void updateFinished(bool isConfigUpdateStarted);
|
||||
void errorOccurred(const QString &errorMessage);
|
||||
ErrorCode updateServerConfigFromApi(const QString &installationUuid, QJsonObject &serverConfig);
|
||||
|
||||
private:
|
||||
struct ApiPayloadData {
|
||||
@@ -36,11 +26,6 @@ private:
|
||||
ApiPayloadData generateApiPayloadData(const QString &protocol);
|
||||
QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData);
|
||||
void processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config);
|
||||
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
|
||||
bool m_isConfigUpdateStarted = false;
|
||||
};
|
||||
|
||||
#endif // APICONTROLLER_H
|
||||
@@ -23,13 +23,13 @@
|
||||
#include <thread>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "logger.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 <configurators/vpn_configurator.h>
|
||||
#include "vpnConfigurationController.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -95,10 +95,9 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||
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)
|
||||
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";
|
||||
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
||||
@@ -116,9 +115,8 @@ ServerController::runContainerScript(const ServerCredentials &credentials, Docke
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &file, const QString &path,
|
||||
libssh::ScpOverwriteMode overwriteMode)
|
||||
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));
|
||||
@@ -156,12 +154,10 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
e = runScript(
|
||||
credentials,
|
||||
replaceVars(
|
||||
QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e)
|
||||
return e;
|
||||
@@ -172,21 +168,17 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
return ErrorCode::ServerContainerMissingError;
|
||||
}
|
||||
|
||||
runScript(credentials,
|
||||
replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||
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)
|
||||
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::NoError;
|
||||
errorCode = ErrorCode::NoError;
|
||||
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(path);
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -194,12 +186,12 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
*errorCode = runScript(credentials, script, cbReadStdOut);
|
||||
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)
|
||||
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) {
|
||||
@@ -245,12 +237,10 @@ ErrorCode ServerController::removeAllContainers(const ServerCredentials &credent
|
||||
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
return runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
|
||||
genVarsForScript(credentials, container)));
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), genVarsForScript(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config, bool isUpdate)
|
||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
|
||||
{
|
||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
@@ -310,12 +300,11 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
return startupContainerWorker(credentials, container, config);
|
||||
}
|
||||
|
||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &oldConfig, QJsonObject &newConfig)
|
||||
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;
|
||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
|
||||
|
||||
if (reinstallRequired) {
|
||||
return setupContainer(credentials, container, newConfig, true);
|
||||
@@ -328,8 +317,7 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
|
||||
const QJsonObject &newConfig)
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
|
||||
{
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
|
||||
@@ -359,7 +347,33 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Awg) {
|
||||
return true;
|
||||
if ((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)))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::WireGuard) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -382,8 +396,7 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||
};
|
||||
|
||||
ErrorCode error =
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||
@@ -395,17 +408,13 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
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)));
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
{
|
||||
ErrorCode e = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),
|
||||
amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
|
||||
@@ -418,13 +427,9 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
if (e)
|
||||
return e;
|
||||
@@ -432,17 +437,13 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config)
|
||||
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;
|
||||
};
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
|
||||
ErrorCode e = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
@@ -459,8 +460,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config)
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -477,13 +477,12 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
|
||||
genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
m_configurator->updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||
VpnConfigurationsController::updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
{
|
||||
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
||||
|
||||
@@ -491,8 +490,7 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials,
|
||||
replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||
"/opt/amnezia/start.sh");
|
||||
if (e)
|
||||
return e;
|
||||
@@ -503,14 +501,15 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||
genVarsForScript(credentials, container, config)));
|
||||
}
|
||||
|
||||
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &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();
|
||||
|
||||
Vars vars;
|
||||
@@ -518,24 +517,19 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
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_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) } });
|
||||
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_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);
|
||||
@@ -546,39 +540,35 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
}
|
||||
|
||||
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
|
||||
openvpnConfig.value(config_key::additional_client_config)
|
||||
.toString(protocols::openvpn::defaultAdditionalClientConfig) } });
|
||||
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) } });
|
||||
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_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({ { "$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) } });
|
||||
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) } });
|
||||
|
||||
// Wireguard vars
|
||||
vars.append(
|
||||
{ { "$WIREGUARD_SUBNET_IP",
|
||||
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
||||
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) } });
|
||||
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" } });
|
||||
@@ -601,32 +591,24 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
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_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_SERVER_PORT",
|
||||
amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
||||
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({ { "$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() } });
|
||||
|
||||
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
||||
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
|
||||
if (!serverIp.isEmpty()) {
|
||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||
} else {
|
||||
@@ -636,7 +618,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
return vars;
|
||||
}
|
||||
|
||||
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode)
|
||||
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode errorCode)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -648,11 +630,7 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
ErrorCode e =
|
||||
runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
errorCode = runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
return stdOut;
|
||||
}
|
||||
@@ -664,9 +642,7 @@ void ServerController::cancelInstallation()
|
||||
|
||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||
{
|
||||
return runScript(
|
||||
credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
||||
}
|
||||
|
||||
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
@@ -678,8 +654,7 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
return s;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
{
|
||||
if (container == DockerContainer::Dns) {
|
||||
return ErrorCode::NoError;
|
||||
@@ -702,15 +677,12 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
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 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 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
for (auto &port : fixedPorts) {
|
||||
script = script.append("|:%1").arg(port);
|
||||
}
|
||||
@@ -720,8 +692,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
script = script.append(" | grep LISTEN");
|
||||
}
|
||||
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
@@ -749,8 +720,7 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
};
|
||||
|
||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||
ErrorCode error =
|
||||
runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (!stdOut.contains("sudo"))
|
||||
return ErrorCode::ServerUserNotInSudo;
|
||||
@@ -780,9 +750,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||
return ErrorCode::ServerCancelInstallation;
|
||||
}
|
||||
stdOut.clear();
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
|
||||
genVarsForScript(credentials)),
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (stdOut.contains("Packet manager not found"))
|
||||
@@ -813,147 +781,6 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||
return future.result();
|
||||
}
|
||||
|
||||
ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
QMap<DockerContainer, QJsonObject> &installedContainers)
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
QString script = QString("sudo docker ps --format '{{.Names}} {{.Ports}}'");
|
||||
|
||||
ErrorCode errorCode = runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
auto containersInfo = stdOut.split("\n");
|
||||
for (auto &containerInfo : containersInfo) {
|
||||
if (containerInfo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
|
||||
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
|
||||
if (containerAndPortMatch.hasMatch()) {
|
||||
QString name = containerAndPortMatch.captured(1);
|
||||
QString port = containerAndPortMatch.captured(2);
|
||||
QString transportProto = containerAndPortMatch.captured(3);
|
||||
DockerContainer container = ContainerProps::containerFromString(name);
|
||||
|
||||
QJsonObject config;
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QJsonObject containerConfig;
|
||||
if (protocol == mainProto) {
|
||||
containerConfig.insert(config_key::port, port);
|
||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||
|
||||
if (protocol == Proto::Awg) {
|
||||
QString serverConfig = getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, &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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
|
||||
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
|
||||
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
|
||||
containerConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
|
||||
containerConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
|
||||
containerConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
|
||||
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
|
||||
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||
containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
|
||||
} else if (protocol == Proto::Sftp) {
|
||||
stdOut.clear();
|
||||
script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name);
|
||||
|
||||
ErrorCode errorCode = runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
auto sftpInfo = stdOut.split(":");
|
||||
if (sftpInfo.size() < 2) {
|
||||
logger.error() << "Key parameters for the sftp container are missing";
|
||||
continue;
|
||||
}
|
||||
auto userName = sftpInfo.at(0);
|
||||
userName = userName.remove(0, 1);
|
||||
auto password = sftpInfo.at(1);
|
||||
|
||||
containerConfig.insert(config_key::userName, userName);
|
||||
containerConfig.insert(config_key::password, password);
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
}
|
||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||
}
|
||||
installedContainers.insert(container, config);
|
||||
}
|
||||
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
|
||||
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
|
||||
if (torOrDnsRegMatch.hasMatch()) {
|
||||
QString name = torOrDnsRegMatch.captured(1);
|
||||
QString port = torOrDnsRegMatch.captured(2);
|
||||
QString transportProto = torOrDnsRegMatch.captured(3);
|
||||
DockerContainer container = ContainerProps::containerFromString(name);
|
||||
|
||||
QJsonObject config;
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QJsonObject containerConfig;
|
||||
if (protocol == mainProto) {
|
||||
containerConfig.insert(config_key::port, port);
|
||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||
|
||||
if (protocol == Proto::TorWebSite) {
|
||||
stdOut.clear();
|
||||
script = QString("sudo docker exec -i %1 sh -c 'cat /var/lib/tor/hidden_service/hostname'").arg(name);
|
||||
|
||||
ErrorCode errorCode = runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
logger.error() << "Key parameters for the tor container are missing";
|
||||
continue;
|
||||
}
|
||||
|
||||
QString onion = stdOut;
|
||||
onion.replace("\n", "");
|
||||
containerConfig.insert(config_key::site, onion);
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
}
|
||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||
}
|
||||
installedContainers.insert(container, config);
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||
const std::function<QString()> &callback)
|
||||
{
|
||||
|
||||
@@ -25,22 +25,18 @@ public:
|
||||
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 getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
QMap<DockerContainer, QJsonObject> &installedContainers);
|
||||
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 = nullptr);
|
||||
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,
|
||||
@@ -50,12 +46,11 @@ public:
|
||||
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);
|
||||
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 = nullptr);
|
||||
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode errorCode);
|
||||
|
||||
void cancelInstallation();
|
||||
|
||||
@@ -64,18 +59,14 @@ public:
|
||||
|
||||
private:
|
||||
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
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 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 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);
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#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));
|
||||
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,
|
||||
ErrorCode errorCode)
|
||||
{
|
||||
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();
|
||||
vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#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, ErrorCode errorCode);
|
||||
|
||||
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
|
||||
@@ -22,6 +22,20 @@ namespace amnezia
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum ErrorCode {
|
||||
// General error codes
|
||||
NoError = 0,
|
||||
@@ -59,6 +73,8 @@ namespace amnezia
|
||||
CloakExecutableMissing = 602,
|
||||
AmneziaServiceConnectionFailed = 603,
|
||||
ExecutableMissing = 604,
|
||||
XrayExecutableMissing = 605,
|
||||
Tun2SockExecutableMissing = 606,
|
||||
|
||||
// VPN errors
|
||||
OpenVpnAdaptersInUseError = 700,
|
||||
@@ -70,6 +86,8 @@ namespace amnezia
|
||||
OpenSslFailed = 800,
|
||||
ShadowSocksExecutableCrashed = 801,
|
||||
CloakExecutableCrashed = 802,
|
||||
XrayExecutableCrashed = 803,
|
||||
Tun2SockExecutableCrashed = 804,
|
||||
|
||||
// import and install errors
|
||||
ImportInvalidConfigError = 900,
|
||||
|
||||
@@ -19,6 +19,7 @@ QString errorString(ErrorCode code) {
|
||||
case(ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
||||
case(ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||
case(ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
||||
case(ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "installedAppsImageProvider.h"
|
||||
|
||||
#include "platforms/android/android_controller.h"
|
||||
|
||||
InstalledAppsImageProvider::InstalledAppsImageProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap)
|
||||
{
|
||||
}
|
||||
|
||||
QPixmap InstalledAppsImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
|
||||
{
|
||||
return AndroidController::instance()->getAppIcon(id, size, requestedSize);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef INSTALLEDAPPSIMAGEPROVIDER_H
|
||||
#define INSTALLEDAPPSIMAGEPROVIDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuickImageProvider>
|
||||
|
||||
class InstalledAppsImageProvider : public QQuickImageProvider
|
||||
{
|
||||
public:
|
||||
InstalledAppsImageProvider();
|
||||
|
||||
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
|
||||
};
|
||||
|
||||
#endif // INSTALLEDAPPSIMAGEPROVIDER_H
|
||||
@@ -71,7 +71,7 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||
}
|
||||
|
||||
QRemoteObjectPendingReply<int> futureResult = Instance()->m_ipcClient->createPrivilegedProcess();
|
||||
futureResult.waitForFinished(1000);
|
||||
futureResult.waitForFinished(5000);
|
||||
|
||||
int pid = futureResult.returnValue();
|
||||
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
#include "networkUtilities.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <Ipexport.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#include <ws2ipdef.h>
|
||||
#include <stdint.h>
|
||||
#include <Iphlpapi.h>
|
||||
#include <Iptypes.h>
|
||||
#include <WinSock2.h>
|
||||
#include <winsock.h>
|
||||
#include <QNetworkInterface>
|
||||
#include "qendian.h"
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/route.h>
|
||||
#endif
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QHostInfo>
|
||||
|
||||
QRegularExpression NetworkUtilities::ipAddressRegExp()
|
||||
{
|
||||
return QRegularExpression("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$");
|
||||
}
|
||||
|
||||
QRegularExpression NetworkUtilities::ipAddressPortRegExp()
|
||||
{
|
||||
return QRegularExpression("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}"
|
||||
"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(\\:[0-9]{1,5}){0,1}$");
|
||||
}
|
||||
|
||||
QRegExp NetworkUtilities::ipAddressWithSubnetRegExp()
|
||||
{
|
||||
return QRegExp("(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}"
|
||||
"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(\\/[0-9]{1,2}){0,1}");
|
||||
}
|
||||
|
||||
QRegExp NetworkUtilities::ipNetwork24RegExp()
|
||||
{
|
||||
return QRegExp("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}"
|
||||
"0$");
|
||||
}
|
||||
|
||||
QRegExp NetworkUtilities::ipPortRegExp()
|
||||
{
|
||||
return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$");
|
||||
}
|
||||
|
||||
QRegExp NetworkUtilities::domainRegExp()
|
||||
{
|
||||
return QRegExp("(((?!\\-))(xn\\-\\-)?[a-z0-9\\-_]{0,61}[a-z0-9]{1,1}\\.)*(xn\\-\\-)?([a-z0-9\\-]{1,61}|[a-z0-"
|
||||
"9\\-]{1,30})\\.[a-z]{2,}");
|
||||
}
|
||||
|
||||
QString NetworkUtilities::netMaskFromIpWithSubnet(const QString ip)
|
||||
{
|
||||
if (!ip.contains("/"))
|
||||
return "255.255.255.255";
|
||||
|
||||
bool ok;
|
||||
int prefix = ip.split("/").at(1).toInt(&ok);
|
||||
if (!ok)
|
||||
return "255.255.255.255";
|
||||
|
||||
unsigned long mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;
|
||||
|
||||
return QString("%1.%2.%3.%4").arg(mask >> 24).arg((mask >> 16) & 0xFF).arg((mask >> 8) & 0xFF).arg(mask & 0xFF);
|
||||
}
|
||||
|
||||
QString NetworkUtilities::ipAddressFromIpWithSubnet(const QString ip)
|
||||
{
|
||||
if (ip.count(".") != 3)
|
||||
return "";
|
||||
return ip.split("/").first();
|
||||
}
|
||||
|
||||
QStringList NetworkUtilities::summarizeRoutes(const QStringList &ips, const QString cidr)
|
||||
{
|
||||
// QMap<int, int>
|
||||
// QHostAddress
|
||||
|
||||
// QMap<QString, QStringList> subnets; // <"a.b", <list subnets>>
|
||||
|
||||
// for (const QString &ip : ips) {
|
||||
// if (ip.count(".") != 3) continue;
|
||||
|
||||
// const QStringList &parts = ip.split(".");
|
||||
// subnets[parts.at(0) + "." + parts.at(1)].append(ip);
|
||||
// }
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString NetworkUtilities::getIPAddress(const QString &host)
|
||||
{
|
||||
if (ipAddressRegExp().match(host).hasMatch()) {
|
||||
return host;
|
||||
}
|
||||
|
||||
QList<QHostAddress> addresses = QHostInfo::fromName(host).addresses();
|
||||
if (!addresses.isEmpty()) {
|
||||
return addresses.first().toString();
|
||||
}
|
||||
qDebug() << "Unable to resolve address for " << host;
|
||||
return "";
|
||||
}
|
||||
|
||||
QString NetworkUtilities::getStringBetween(const QString &s, const QString &a, const QString &b)
|
||||
{
|
||||
int ap = s.indexOf(a), bp = s.indexOf(b, ap + a.length());
|
||||
if (ap < 0 || bp < 0)
|
||||
return QString();
|
||||
ap += a.length();
|
||||
if (bp - ap <= 0)
|
||||
return QString();
|
||||
return s.mid(ap, bp - ap).trimmed();
|
||||
}
|
||||
|
||||
bool NetworkUtilities::checkIPv4Format(const QString &ip)
|
||||
{
|
||||
if (ip.isEmpty())
|
||||
return false;
|
||||
int count = ip.count(".");
|
||||
if (count != 3)
|
||||
return false;
|
||||
|
||||
QHostAddress addr(ip);
|
||||
return (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol);
|
||||
}
|
||||
|
||||
bool NetworkUtilities::checkIpSubnetFormat(const QString &ip)
|
||||
{
|
||||
if (!ip.contains("/"))
|
||||
return checkIPv4Format(ip);
|
||||
|
||||
QStringList parts = ip.split("/");
|
||||
if (parts.size() != 2)
|
||||
return false;
|
||||
|
||||
bool ok;
|
||||
int subnet = parts.at(1).toInt(&ok);
|
||||
if (subnet >= 0 && subnet <= 32 && ok)
|
||||
return checkIPv4Format(parts.at(0));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) {
|
||||
#ifdef Q_OS_WIN
|
||||
qDebug() << "Getting Current Internet Adapter that routes to"
|
||||
<< dst.toString();
|
||||
quint32_be ipBigEndian;
|
||||
quint32 ip = dst.toIPv4Address();
|
||||
qToBigEndian(ip, &ipBigEndian);
|
||||
_MIB_IPFORWARDROW routeInfo;
|
||||
auto result = GetBestRoute(ipBigEndian, 0, &routeInfo);
|
||||
if (result != NO_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
auto adapter =
|
||||
QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex);
|
||||
qDebug() << "Internet Adapter:" << adapter.name();
|
||||
return routeInfo.dwForwardIfIndex;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
||||
const ULONG Flags,
|
||||
const PVOID Reserved,
|
||||
_Out_ PIP_ADAPTER_ADDRESSES& pAdapterAddresses) {
|
||||
DWORD dwRetVal = 0;
|
||||
int iter = 0;
|
||||
constexpr int max_iter = 3;
|
||||
ULONG AdapterAddressesLen = 15000;
|
||||
do {
|
||||
// xassert2(pAdapterAddresses == nullptr);
|
||||
pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)malloc(AdapterAddressesLen);
|
||||
if (pAdapterAddresses == nullptr) {
|
||||
qDebug() << "can not malloc" << AdapterAddressesLen << "bytes";
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
dwRetVal = GetAdaptersAddresses(Family, Flags, NULL, pAdapterAddresses, &AdapterAddressesLen);
|
||||
|
||||
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
|
||||
free(pAdapterAddresses);
|
||||
pAdapterAddresses = nullptr;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
iter++;
|
||||
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (iter < max_iter));
|
||||
|
||||
if (dwRetVal != NO_ERROR) {
|
||||
qDebug() << "Family: " << Family << ", Flags: " << Flags << " AdapterAddressesLen: " << AdapterAddressesLen <<
|
||||
", dwRetVal:" << dwRetVal << ", iter: " << iter;
|
||||
if (pAdapterAddresses) {
|
||||
free(pAdapterAddresses);
|
||||
pAdapterAddresses = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return dwRetVal;
|
||||
}
|
||||
#endif
|
||||
|
||||
QString NetworkUtilities::getGatewayAndIface()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
constexpr int BUFF_LEN = 100;
|
||||
char buff[BUFF_LEN] = {'\0'};
|
||||
QString result;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pAdapterAddresses = nullptr;
|
||||
DWORD dwRetVal =
|
||||
GetAdaptersAddressesWrapper(AF_INET, GAA_FLAG_INCLUDE_GATEWAYS, NULL, pAdapterAddresses);
|
||||
|
||||
if (dwRetVal != NO_ERROR) {
|
||||
qDebug() << "ipv4 stack detect GetAdaptersAddresses failed.";
|
||||
return "";
|
||||
}
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pCurAddress = pAdapterAddresses;
|
||||
while (pCurAddress) {
|
||||
PIP_ADAPTER_GATEWAY_ADDRESS_LH gateway = pCurAddress->FirstGatewayAddress;
|
||||
if (gateway) {
|
||||
SOCKET_ADDRESS gateway_address = gateway->Address;
|
||||
if (gateway->Address.lpSockaddr->sa_family == AF_INET) {
|
||||
sockaddr_in* sa_in = (sockaddr_in*)gateway->Address.lpSockaddr;
|
||||
QString gw = inet_ntop(AF_INET, &(sa_in->sin_addr), buff, BUFF_LEN);
|
||||
qDebug() << "gateway IPV4:" << gw;
|
||||
struct sockaddr_in addr;
|
||||
if (inet_pton(AF_INET, buff, &addr.sin_addr) == 1) {
|
||||
qDebug() << "this is true v4 !";
|
||||
result = gw;
|
||||
}
|
||||
}
|
||||
}
|
||||
pCurAddress = pCurAddress->Next;
|
||||
}
|
||||
|
||||
free(pAdapterAddresses);
|
||||
return result;
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
constexpr int BUFFER_SIZE = 100;
|
||||
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
|
||||
int sock = -1, msgseq = 0;
|
||||
struct nlmsghdr *nlh, *nlmsg;
|
||||
struct rtmsg *route_entry;
|
||||
// This struct contain route attributes (route type)
|
||||
struct rtattr *route_attribute;
|
||||
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
|
||||
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
|
||||
char *ptr = buffer;
|
||||
struct timeval tv;
|
||||
|
||||
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
perror("socket failed");
|
||||
return "";
|
||||
}
|
||||
|
||||
memset(msgbuf, 0, sizeof(msgbuf));
|
||||
memset(gateway_address, 0, sizeof(gateway_address));
|
||||
memset(interface, 0, sizeof(interface));
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
/* point the header and the msg structure pointers into the buffer */
|
||||
nlmsg = (struct nlmsghdr *)msgbuf;
|
||||
|
||||
/* Fill in the nlmsg header*/
|
||||
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
|
||||
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
|
||||
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
|
||||
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
|
||||
|
||||
/* 1 Sec Timeout to avoid stall */
|
||||
tv.tv_sec = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||
/* send msg */
|
||||
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
|
||||
perror("send failed");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* receive response */
|
||||
do
|
||||
{
|
||||
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
|
||||
if (received_bytes < 0) {
|
||||
perror("Error in recv");
|
||||
return "";
|
||||
}
|
||||
|
||||
nlh = (struct nlmsghdr *) ptr;
|
||||
|
||||
/* Check if the header is valid */
|
||||
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
|
||||
(nlmsg->nlmsg_type == NLMSG_ERROR))
|
||||
{
|
||||
perror("Error in received packet");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* If we received all data break */
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
else {
|
||||
ptr += received_bytes;
|
||||
msg_len += received_bytes;
|
||||
}
|
||||
|
||||
/* Break if its not a multi part message */
|
||||
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
|
||||
break;
|
||||
}
|
||||
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
|
||||
|
||||
/* parse response */
|
||||
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
|
||||
{
|
||||
/* Get the route data */
|
||||
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
|
||||
|
||||
/* We are just interested in main routing table */
|
||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
|
||||
route_attribute_len = RTM_PAYLOAD(nlh);
|
||||
|
||||
/* Loop through all attributes */
|
||||
for ( ; RTA_OK(route_attribute, route_attribute_len);
|
||||
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
|
||||
{
|
||||
switch(route_attribute->rta_type) {
|
||||
case RTA_OIF:
|
||||
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
inet_ntop(AF_INET, RTA_DATA(route_attribute),
|
||||
gateway_address, sizeof(gateway_address));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*gateway_address) && (*interface)) {
|
||||
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
return gateway_address;
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
||||
QString gateway;
|
||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
int afinet_type[] = {AF_INET, AF_INET6};
|
||||
|
||||
for (int ip_type = 0; ip_type <= 1; ip_type++)
|
||||
{
|
||||
mib[3] = afinet_type[ip_type];
|
||||
|
||||
size_t needed = 0;
|
||||
if (sysctl(mib, sizeof(mib) / sizeof(int), nullptr, &needed, nullptr, 0) < 0)
|
||||
return "";
|
||||
|
||||
char* buf;
|
||||
if ((buf = new char[needed]) == 0)
|
||||
return "";
|
||||
|
||||
if (sysctl(mib, sizeof(mib) / sizeof(int), buf, &needed, nullptr, 0) < 0)
|
||||
{
|
||||
qDebug() << "sysctl: net.route.0.0.dump";
|
||||
delete[] buf;
|
||||
return gateway;
|
||||
}
|
||||
|
||||
struct rt_msghdr* rt;
|
||||
for (char* p = buf; p < buf + needed; p += rt->rtm_msglen)
|
||||
{
|
||||
rt = reinterpret_cast<struct rt_msghdr*>(p);
|
||||
struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(rt + 1);
|
||||
struct sockaddr* sa_tab[RTAX_MAX];
|
||||
for (int i = 0; i < RTAX_MAX; i++)
|
||||
{
|
||||
if (rt->rtm_addrs & (1 << i))
|
||||
{
|
||||
sa_tab[i] = sa;
|
||||
sa = reinterpret_cast<struct sockaddr*>(
|
||||
reinterpret_cast<char*>(sa) +
|
||||
((sa->sa_len) > 0 ? (1 + (((sa->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long)));
|
||||
}
|
||||
else
|
||||
{
|
||||
sa_tab[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) == (RTA_DST | RTA_GATEWAY)) &&
|
||||
sa_tab[RTAX_DST]->sa_family == afinet_type[ip_type] &&
|
||||
sa_tab[RTAX_GATEWAY]->sa_family == afinet_type[ip_type])
|
||||
{
|
||||
if (afinet_type[ip_type] == AF_INET)
|
||||
{
|
||||
if ((reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0)
|
||||
{
|
||||
char dstStr4[INET_ADDRSTRLEN];
|
||||
char srcStr4[INET_ADDRSTRLEN];
|
||||
memcpy(srcStr4,
|
||||
&(reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_GATEWAY]))->sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
if (inet_ntop(AF_INET, srcStr4, dstStr4, INET_ADDRSTRLEN) != nullptr)
|
||||
gateway = dstStr4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (afinet_type[ip_type] == AF_INET6)
|
||||
{
|
||||
if ((reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0)
|
||||
{
|
||||
char dstStr6[INET6_ADDRSTRLEN];
|
||||
char srcStr6[INET6_ADDRSTRLEN];
|
||||
memcpy(srcStr6,
|
||||
&(reinterpret_cast<struct sockaddr_in6*>(sa_tab[RTAX_GATEWAY]))->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
if (inet_ntop(AF_INET6, srcStr6, dstStr6, INET6_ADDRSTRLEN) != nullptr)
|
||||
gateway = dstStr6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return gateway;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef NETWORKUTILITIES_H
|
||||
#define NETWORKUTILITIES_H
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
#include <QHostAddress>
|
||||
|
||||
|
||||
class NetworkUtilities : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QString getIPAddress(const QString &host);
|
||||
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
||||
static bool checkIPv4Format(const QString &ip);
|
||||
static bool checkIpSubnetFormat(const QString &ip);
|
||||
static QString getGatewayAndIface();
|
||||
// Returns the Interface Index that could Route to dst
|
||||
static int AdapterIndexTo(const QHostAddress& dst);
|
||||
|
||||
static QRegularExpression ipAddressRegExp();
|
||||
static QRegularExpression ipAddressPortRegExp();
|
||||
static QRegExp ipAddressWithSubnetRegExp();
|
||||
static QRegExp ipNetwork24RegExp();
|
||||
static QRegExp ipPortRegExp();
|
||||
static QRegExp domainRegExp();
|
||||
|
||||
static QString netMaskFromIpWithSubnet(const QString ip);
|
||||
static QString ipAddressFromIpWithSubnet(const QString ip);
|
||||
|
||||
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
||||
|
||||
};
|
||||
|
||||
#endif // NETWORKUTILITIES_H
|
||||
@@ -13,11 +13,12 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
||||
case DockerContainer::WireGuard: return QLatin1String("wireguard");
|
||||
case DockerContainer::Awg: return QLatin1String("awg");
|
||||
case DockerContainer::Ipsec: return QLatin1String("ipsec");
|
||||
case DockerContainer::Xray: return QLatin1String("xray");
|
||||
|
||||
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
|
||||
case DockerContainer::Dns: return QLatin1String("dns");
|
||||
case DockerContainer::Sftp: return QLatin1String("sftp");
|
||||
default: return "";
|
||||
default: return QString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +48,7 @@ QString amnezia::scriptName(ProtocolScriptType type)
|
||||
case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn");
|
||||
case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf");
|
||||
case ProtocolScriptType::awg_template: return QLatin1String("template.conf");
|
||||
case ProtocolScriptType::xray_template: return QLatin1String("template.json");
|
||||
default: return QString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ enum ProtocolScriptType {
|
||||
container_startup,
|
||||
openvpn_template,
|
||||
wireguard_template,
|
||||
awg_template
|
||||
awg_template,
|
||||
xray_template
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ namespace libssh {
|
||||
|
||||
ErrorCode Client::connectToHost(const ServerCredentials &credentials)
|
||||
{
|
||||
if (m_session != nullptr) {
|
||||
if (!ssh_is_connected(m_session)) {
|
||||
ssh_free(m_session);
|
||||
m_session = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_session == nullptr) {
|
||||
m_session = ssh_new();
|
||||
|
||||
|
||||
@@ -248,9 +248,23 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
|
||||
GETVALUE("privateKey", config.m_privateKey, String);
|
||||
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
|
||||
GETVALUE("serverPskKey", config.m_serverPskKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
|
||||
config.m_serverPskKey = obj.value("serverPskKey").toString();
|
||||
|
||||
if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0)
|
||||
{
|
||||
config.m_deviceMTU = 1420;
|
||||
} else {
|
||||
config.m_deviceMTU = obj.value("deviceMTU").toString().toInt();
|
||||
#ifdef Q_OS_WINDOWS
|
||||
// For Windows min MTU value is 1280 (the smallest MTU legal with IPv6).
|
||||
if (config.m_deviceMTU < 1280) {
|
||||
config.m_deviceMTU = 1280;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString();
|
||||
config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString();
|
||||
if (config.m_deviceIpv4Address.isNull() &&
|
||||
@@ -360,19 +374,33 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!obj.value("Jc").isNull() && !obj.value("Jmin").isNull()
|
||||
&& !obj.value("Jmax").isNull() && !obj.value("S1").isNull()
|
||||
&& !obj.value("S2").isNull() && !obj.value("H1").isNull()
|
||||
&& !obj.value("H2").isNull() && !obj.value("H3").isNull()
|
||||
&& !obj.value("H4").isNull()) {
|
||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||
|
||||
if (!obj.value("Jc").isNull()) {
|
||||
config.m_junkPacketCount = obj.value("Jc").toString();
|
||||
}
|
||||
if (!obj.value("Jmin").isNull()) {
|
||||
config.m_junkPacketMinSize = obj.value("Jmin").toString();
|
||||
}
|
||||
if (!obj.value("Jmax").isNull()) {
|
||||
config.m_junkPacketMaxSize = obj.value("Jmax").toString();
|
||||
}
|
||||
if (!obj.value("S1").isNull()) {
|
||||
config.m_initPacketJunkSize = obj.value("S1").toString();
|
||||
}
|
||||
if (!obj.value("S2").isNull()) {
|
||||
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
||||
}
|
||||
if (!obj.value("H1").isNull()) {
|
||||
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
||||
}
|
||||
if (!obj.value("H2").isNull()) {
|
||||
config.m_responsePacketMagicHeader = obj.value("H2").toString();
|
||||
}
|
||||
if (!obj.value("H3").isNull()) {
|
||||
config.m_underloadPacketMagicHeader = obj.value("H3").toString();
|
||||
}
|
||||
if (!obj.value("H4").isNull()) {
|
||||
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ class Daemon : public QObject {
|
||||
virtual QJsonObject getStatus();
|
||||
|
||||
// Callback before any Activating measure is done
|
||||
virtual void prepareActivation(const InterfaceConfig& config){
|
||||
Q_UNUSED(config)};
|
||||
virtual void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) {
|
||||
Q_UNUSED(config) };
|
||||
virtual void activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) {
|
||||
Q_UNUSED(config) };
|
||||
|
||||
QString logs();
|
||||
void cleanLogs();
|
||||
|
||||
@@ -23,6 +23,7 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||
json.insert("serverIpv4AddrIn", QJsonValue(m_serverIpv4AddrIn));
|
||||
json.insert("serverIpv6AddrIn", QJsonValue(m_serverIpv6AddrIn));
|
||||
json.insert("serverPort", QJsonValue((double)m_serverPort));
|
||||
json.insert("deviceMTU", QJsonValue(m_deviceMTU));
|
||||
if ((m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||
(m_hopType == InterfaceConfig::SingleHop)) {
|
||||
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
||||
@@ -85,8 +86,13 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||
if (addresses.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
out << "Address = " << addresses.join(", ") << "\n";
|
||||
|
||||
if (m_deviceMTU) {
|
||||
out << "MTU = " << m_deviceMTU << "\n";
|
||||
}
|
||||
|
||||
if (!m_dnsServer.isNull()) {
|
||||
QStringList dnsServers(m_dnsServer);
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
|
||||
@@ -33,9 +33,11 @@ class InterfaceConfig {
|
||||
QString m_serverIpv6AddrIn;
|
||||
QString m_dnsServer;
|
||||
int m_serverPort = 0;
|
||||
int m_deviceMTU = 1420;
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
bool m_killSwitchEnabled;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
QString m_installationId;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 8V12" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 16H12.01" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 518 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V7.5L14.5 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V8" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 2V8H20" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 15L5 17L9 13" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 581 B |
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -84,11 +84,20 @@ target_sources(networkextension PRIVATE
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPNAdapterDelegate.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
||||
)
|
||||
|
||||
target_sources(networkextension PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
set_property(TARGET networkextension APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
## Build wireguard-go-version.h
|
||||
execute_process(
|
||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+2
-1
@@ -26,9 +26,10 @@ int main(int argc, char *argv[])
|
||||
AllowSetForegroundWindow(ASFW_ANY);
|
||||
#endif
|
||||
|
||||
// QTBUG-95974 QTBUG-95764 QTBUG-102168
|
||||
#ifdef Q_OS_ANDROID
|
||||
// QTBUG-95974 QTBUG-95764 QTBUG-102168
|
||||
qputenv("QT_ANDROID_DISABLE_ACCESSIBILITY", "1");
|
||||
qputenv("ANDROID_OPENSSL_SUFFIX", "_3");
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
|
||||
@@ -117,6 +117,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
|
||||
QJsonArray splitTunnelSites = rawConfig.value("splitTunnelSites").toArray();
|
||||
|
||||
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
||||
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
||||
|
||||
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||
|
||||
QJsonObject json;
|
||||
@@ -124,16 +127,21 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
|
||||
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
|
||||
// todo review wg ipv6
|
||||
#ifdef Q_OS_MACOS
|
||||
json.insert("deviceIpv6Address", "dead::1");
|
||||
#endif
|
||||
|
||||
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
|
||||
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4.
|
||||
// Otherwise some OSes (Linux) try IPv6 forever and hang.
|
||||
// https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193)
|
||||
// https://man7.org/linux/man-pages/man5/gai.conf.5.html
|
||||
json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it
|
||||
|
||||
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
|
||||
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
|
||||
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn()));
|
||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
json.insert("deviceMTU", wgConfig.value(amnezia::config_key::mtu));
|
||||
|
||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
@@ -212,13 +220,10 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||
|
||||
// QJsonArray splitTunnelApps;
|
||||
// for (const auto& uri : hop.m_vpnDisabledApps) {
|
||||
// splitTunnelApps.append(QJsonValue(uri));
|
||||
// }
|
||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
if (protocolName == amnezia::config_key::awg) {
|
||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||
@@ -229,6 +234,24 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
} else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) {
|
||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
}
|
||||
|
||||
write(json);
|
||||
|
||||
@@ -171,9 +171,15 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
}
|
||||
|
||||
|
||||
QString NetworkWatcher::getCurrentTransport() {
|
||||
auto type = m_impl->getTransportType();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
|
||||
return QString(metaEnum.valueToKey(type))
|
||||
.remove("TransportType_", Qt::CaseSensitive);
|
||||
QNetworkInformation::Reachability NetworkWatcher::getReachability() {
|
||||
if (m_simulatedDisconnection) {
|
||||
return QNetworkInformation::Reachability::Disconnected;
|
||||
} else if (QNetworkInformation::instance()) {
|
||||
return QNetworkInformation::instance()->reachability();
|
||||
}
|
||||
return QNetworkInformation::Reachability::Unknown;
|
||||
}
|
||||
|
||||
void NetworkWatcher::simulateDisconnection(bool simulatedDisconnection) {
|
||||
m_simulatedDisconnection = simulatedDisconnection;
|
||||
}
|
||||
|
||||
@@ -7,45 +7,50 @@
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QNetworkInformation>
|
||||
|
||||
|
||||
class NetworkWatcherImpl;
|
||||
|
||||
// This class watches for network changes to detect unsecured wifi.
|
||||
class NetworkWatcher final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
|
||||
void initialize();
|
||||
void initialize();
|
||||
|
||||
// public for the inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Public for the Inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Used for the Inspector. simulateOffline = true to mock being disconnected,
|
||||
// false to restore.
|
||||
void simulateDisconnection(bool simulatedDisconnection);
|
||||
|
||||
QString getCurrentTransport();
|
||||
QNetworkInformation::Reachability getReachability();
|
||||
|
||||
signals:
|
||||
void networkChange();
|
||||
signals:
|
||||
void networkChange();
|
||||
|
||||
private:
|
||||
void settingsChanged();
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
// void notificationClicked(NotificationHandler::Message message);
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
// Used to simulate network disconnection in the Inspector
|
||||
bool m_simulatedDisconnection = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHER_H
|
||||
|
||||
@@ -5,50 +5,45 @@
|
||||
#ifndef NETWORKWATCHERIMPL_H
|
||||
#define NETWORKWATCHERIMPL_H
|
||||
|
||||
#include <QNetworkInformation>
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
virtual void initialize() = 0;
|
||||
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
|
||||
bool isActive() const { return m_active; }
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
|
||||
// Returns the current type of Network Connection
|
||||
virtual TransportType getTransportType() = 0;
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
// Fired when the Device changed the Type of Transport
|
||||
void transportChanged(NetworkWatcherImpl::TransportType transportType);
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
private:
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHERIMPL_H
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QQmlFile>
|
||||
#include <QEventLoop>
|
||||
#include <QImage>
|
||||
|
||||
#include <android/bitmap.h>
|
||||
|
||||
#include "android_controller.h"
|
||||
#include "android_utils.h"
|
||||
@@ -209,6 +212,51 @@ void AndroidController::setScreenshotsEnabled(bool enabled)
|
||||
callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled);
|
||||
}
|
||||
|
||||
void AndroidController::minimizeApp()
|
||||
{
|
||||
callActivityMethod("minimizeApp", "()V");
|
||||
}
|
||||
|
||||
QJsonArray AndroidController::getAppList()
|
||||
{
|
||||
QJniObject appList = callActivityMethod<jstring>("getAppList", "()Ljava/lang/String;");
|
||||
QJsonArray jsonAppList = QJsonDocument::fromJson(appList.toString().toUtf8()).array();
|
||||
return jsonAppList;
|
||||
}
|
||||
|
||||
QPixmap AndroidController::getAppIcon(const QString &package, QSize *size, const QSize &requestedSize)
|
||||
{
|
||||
QJniObject bitmap = callActivityMethod<jobject>("getAppIcon", "(Ljava/lang/String;II)Landroid/graphics/Bitmap;",
|
||||
QJniObject::fromString(package).object<jstring>(),
|
||||
requestedSize.width(), requestedSize.height());
|
||||
|
||||
QJniEnvironment env;
|
||||
AndroidBitmapInfo info;
|
||||
if (AndroidBitmap_getInfo(env.jniEnv(), bitmap.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
void *pixels;
|
||||
if (AndroidBitmap_lockPixels(env.jniEnv(), bitmap.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
int width = info.width;
|
||||
int height = info.height;
|
||||
|
||||
size->setWidth(width);
|
||||
size->setHeight(height);
|
||||
|
||||
QImage image(width, height, QImage::Format_RGBA8888);
|
||||
if (info.stride == uint32_t(image.bytesPerLine())) {
|
||||
memcpy((void *) image.constBits(), pixels, info.stride * height);
|
||||
} else {
|
||||
auto *bmpPtr = static_cast<uchar *>(pixels);
|
||||
for (int i = 0; i < height; i++, bmpPtr += info.stride)
|
||||
memcpy((void *) image.constScanLine(i), bmpPtr, width);
|
||||
}
|
||||
|
||||
if (AndroidBitmap_unlockPixels(env.jniEnv(), bitmap.object()) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
return QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
// Moving log processing to the Android side
|
||||
jclass AndroidController::log;
|
||||
jmethodID AndroidController::logDebug;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ANDROID_CONTROLLER_H
|
||||
|
||||
#include <QJniObject>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "protocols/vpnprotocol.h"
|
||||
|
||||
@@ -40,6 +41,9 @@ public:
|
||||
void exportLogsFile(const QString &fileName);
|
||||
void clearLogs();
|
||||
void setScreenshotsEnabled(bool enabled);
|
||||
void minimizeApp();
|
||||
QJsonArray getAppList();
|
||||
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
|
||||
|
||||
static bool initLogging();
|
||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent)
|
||||
: NetworkWatcherImpl(parent) {
|
||||
MZ_COUNT_CTOR(DummyNetworkWatcher);
|
||||
MZ_COUNT_CTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
DummyNetworkWatcher::~DummyNetworkWatcher() {
|
||||
MZ_COUNT_DTOR(DummyNetworkWatcher);
|
||||
MZ_COUNT_DTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
void DummyNetworkWatcher::initialize() {}
|
||||
|
||||
@@ -8,15 +8,11 @@
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
class DummyNetworkWatcher final : public NetworkWatcherImpl {
|
||||
public:
|
||||
DummyNetworkWatcher(QObject* parent);
|
||||
~DummyNetworkWatcher();
|
||||
public:
|
||||
DummyNetworkWatcher(QObject* parent);
|
||||
~DummyNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() override {
|
||||
return TransportType_Other;
|
||||
};
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
#endif // DUMMYNETWORKWATCHER_H
|
||||
|
||||
@@ -2,6 +2,8 @@ import Foundation
|
||||
import os.log
|
||||
|
||||
struct Log {
|
||||
static let osLog = Logger()
|
||||
|
||||
private static let IsLoggingEnabledKey = "IsLoggingEnabled"
|
||||
static var isLoggingEnabled: Bool {
|
||||
get {
|
||||
@@ -14,6 +16,11 @@ struct Log {
|
||||
|
||||
private static let appGroupID = "group.org.amnezia.AmneziaVPN"
|
||||
|
||||
static let appLogURL = {
|
||||
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)!
|
||||
return sharedContainerURL.appendingPathComponent("app.log", isDirectory: false)
|
||||
}()
|
||||
|
||||
static let neLogURL = {
|
||||
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)!
|
||||
return sharedContainerURL.appendingPathComponent("ne.log", isDirectory: false)
|
||||
@@ -29,16 +36,23 @@ struct Log {
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
var records: [Record]
|
||||
var records = [Record]()
|
||||
|
||||
var lastRecordDate = Date.distantPast
|
||||
|
||||
init() {
|
||||
self.records = []
|
||||
}
|
||||
|
||||
init(_ str: String) {
|
||||
self.records = str.split(whereSeparator: \.isNewline)
|
||||
.compactMap {
|
||||
Record(String($0))!
|
||||
records = str.split(whereSeparator: \.isNewline)
|
||||
.map {
|
||||
if let record = Record(String($0)) {
|
||||
lastRecordDate = record.date
|
||||
return record
|
||||
} else {
|
||||
return Record(date: lastRecordDate, level: .error, message: "LOG: \($0)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +74,28 @@ struct Log {
|
||||
self.init(str)
|
||||
}
|
||||
|
||||
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
|
||||
NSLog("\(title) \(message)")
|
||||
|
||||
guard isLoggingEnabled else { return }
|
||||
|
||||
osLog.log(level: type, "\(title) \(message)")
|
||||
|
||||
let date = Date()
|
||||
let level = Record.Level(from: type)
|
||||
let messages = message.split(whereSeparator: \.isNewline)
|
||||
|
||||
for index in 0..<messages.count {
|
||||
let message = String(messages[index])
|
||||
|
||||
if index != 0 && message.first != " " {
|
||||
Record(date: date, level: level, message: "\(title) \(message)").save(at: url)
|
||||
} else {
|
||||
Record(date: date, level: level, message: "\(title)\(message)").save(at: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func clear(at url: URL) {
|
||||
if FileManager.default.fileExists(atPath: url.path) {
|
||||
guard let fileHandle = try? FileHandle(forUpdating: url) else { return }
|
||||
@@ -80,3 +116,7 @@ extension Log: CustomStringConvertible {
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func log(_ type: OSLogType, title: String = "", message: String) {
|
||||
Log.log(type, title: "App: \(title)", message: message, url: Log.appLogURL)
|
||||
}
|
||||
|
||||
@@ -1,50 +1,33 @@
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
public func swiftUpdateLogData(_ qtString: std.string) -> std.string {
|
||||
let qtLog = Log(String(describing: qtString))
|
||||
var log = qtLog
|
||||
|
||||
if let appLog = Log(at: Log.appLogURL) {
|
||||
appLog.records.forEach {
|
||||
log.records.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
if let neLog = Log(at: Log.neLogURL) {
|
||||
neLog.records.forEach {
|
||||
log.records.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
log.records.sort {
|
||||
$0.date < $1.date
|
||||
}
|
||||
log.records.sort {
|
||||
$0.date < $1.date
|
||||
}
|
||||
|
||||
return std.string(log.description)
|
||||
}
|
||||
|
||||
public func swiftDeleteLog() {
|
||||
Log.clear(at: Log.appLogURL)
|
||||
Log.clear(at: Log.neLogURL)
|
||||
}
|
||||
|
||||
public func toggleLogging(_ isEnabled: Bool) {
|
||||
Log.isLoggingEnabled = isEnabled
|
||||
}
|
||||
|
||||
public func clearSettings() {
|
||||
NETunnelProviderManager.loadAllFromPreferences { managers, error in
|
||||
if let error {
|
||||
NSLog("clearSettings removeFromPreferences error: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
managers?.forEach { manager in
|
||||
manager.removeFromPreferences { error in
|
||||
if let error {
|
||||
NSLog("NE removeFromPreferences error: \(error.localizedDescription)")
|
||||
} else {
|
||||
manager.loadFromPreferences { error in
|
||||
if let error {
|
||||
NSLog("NE loadFromPreferences after remove error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ extension Log {
|
||||
}
|
||||
|
||||
func save(at url: URL) {
|
||||
osLog.log(level: level.osLogType, "\(message)")
|
||||
|
||||
guard let data = "\n\(description)".data(using: .utf8) else { return }
|
||||
|
||||
if !FileManager.default.fileExists(atPath: url.path) {
|
||||
@@ -64,19 +66,38 @@ extension Log.Record {
|
||||
|
||||
init(from osLogType: OSLogType) {
|
||||
switch osLogType {
|
||||
case OSLogType.default:
|
||||
case .default:
|
||||
self = .info
|
||||
case OSLogType.info:
|
||||
case .info:
|
||||
self = .info
|
||||
case OSLogType.debug:
|
||||
case .debug:
|
||||
self = .debug
|
||||
case OSLogType.error:
|
||||
case .error:
|
||||
self = .error
|
||||
case OSLogType.fault:
|
||||
case .fault:
|
||||
self = .fatal
|
||||
default:
|
||||
self = .info
|
||||
}
|
||||
}
|
||||
|
||||
var osLogType: OSLogType {
|
||||
switch self {
|
||||
case .info:
|
||||
return .info
|
||||
case .debug:
|
||||
return .debug
|
||||
case .error:
|
||||
return .error
|
||||
case .fatal:
|
||||
return .fault
|
||||
case .warning:
|
||||
return .info
|
||||
case .critical:
|
||||
return .fault
|
||||
case .system:
|
||||
return .fault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
public func wg_log(_ type: OSLogType, staticMessage: StaticString) {
|
||||
guard Log.isLoggingEnabled else { return }
|
||||
|
||||
Log.Record(date: Date(), level: Log.Record.Level(from: type), message: "\(staticMessage)").save(at: Log.neLogURL)
|
||||
public func wg_log(_ type: OSLogType, title: String = "", staticMessage: StaticString) {
|
||||
neLog(type, title: "WG: \(title)", message: "\(staticMessage)")
|
||||
}
|
||||
|
||||
public func wg_log(_ type: OSLogType, message: String) {
|
||||
log(type, message: message)
|
||||
public func wg_log(_ type: OSLogType, title: String = "", message: String) {
|
||||
neLog(type, title: "WG: \(title)", message: message)
|
||||
}
|
||||
|
||||
public func log(_ type: OSLogType, message: String) {
|
||||
guard Log.isLoggingEnabled else { return }
|
||||
|
||||
Log.Record(date: Date(), level: Log.Record.Level(from: type), message: message).save(at: Log.neLogURL)
|
||||
public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
|
||||
neLog(type, title: "OVPN: \(title)", message: message)
|
||||
}
|
||||
|
||||
public func neLog(_ type: OSLogType, title: String = "", message: String) {
|
||||
Log.log(type, title: "NE: \(title)", message: message)
|
||||
}
|
||||
|
||||
+105
-1
@@ -2,6 +2,108 @@ import Foundation
|
||||
import NetworkExtension
|
||||
import OpenVPNAdapter
|
||||
|
||||
struct OpenVPNConfig: Decodable {
|
||||
let config: String
|
||||
let splitTunnelType: Int
|
||||
let splitTunnelSites: [String]
|
||||
|
||||
var str: String {
|
||||
"splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)"
|
||||
}
|
||||
}
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
||||
ovpnLog(.error, message: "Can't start")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
// ovpnLog(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self))")
|
||||
|
||||
let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData)
|
||||
ovpnLog(.info, title: "config: ", message: openVPNConfig.str)
|
||||
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
|
||||
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
||||
} catch {
|
||||
ovpnLog(.error, message: "Can't parse config: \(error.localizedDescription)")
|
||||
|
||||
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
|
||||
ovpnLog(.error, message: "Can't parse config: \(underlyingError.localizedDescription)")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
|
||||
withShadowSocks viaSS: Bool = false,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
ovpnLog(.info, message: "Setup and launch")
|
||||
|
||||
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
||||
|
||||
let configuration = OpenVPNConfiguration()
|
||||
configuration.fileContent = ovpnConfiguration
|
||||
if str.contains("cloak") {
|
||||
configuration.setPTCloak()
|
||||
}
|
||||
|
||||
let evaluation: OpenVPNConfigurationEvaluation
|
||||
do {
|
||||
evaluation = try ovpnAdapter.apply(configuration: configuration)
|
||||
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
|
||||
if !evaluation.autologin {
|
||||
ovpnLog(.info, message: "Implement login with user credentials")
|
||||
}
|
||||
|
||||
vpnReachability.startTracking { [weak self] status in
|
||||
guard status == .reachableViaWiFi else { return }
|
||||
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
|
||||
}
|
||||
|
||||
startHandler = completionHandler
|
||||
ovpnAdapter.connect(using: packetFlow)
|
||||
|
||||
// let ifaces = Interface.allInterfaces()
|
||||
// .filter { $0.family == .ipv4 }
|
||||
// .map { iface in iface.name }
|
||||
|
||||
// ovpn_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
||||
}
|
||||
|
||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
let bytesin = ovpnAdapter.transportStatistics.bytesIn
|
||||
let bytesout = ovpnAdapter.transportStatistics.bytesOut
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes": bytesin,
|
||||
"tx_bytes": bytesout
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
|
||||
func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
ovpnLog(.info, message: "Stopping tunnel: reason: \(reason.description)")
|
||||
|
||||
stopHandler = completionHandler
|
||||
if vpnReachability.isTracking {
|
||||
vpnReachability.stopTracking()
|
||||
}
|
||||
ovpnAdapter.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
|
||||
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
|
||||
@@ -116,6 +218,8 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||
// Use this method to process any log message returned by OpenVPN library.
|
||||
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
|
||||
// Handle log messages
|
||||
wg_log(.info, message: logMessage)
|
||||
ovpnLog(.info, message: logMessage)
|
||||
}
|
||||
}
|
||||
|
||||
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
|
||||
@@ -0,0 +1,221 @@
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
func startWireguard(activationAttemptId: String?,
|
||||
errorNotifier: ErrorNotifier,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
||||
wg_log(.error, message: "Can't start, config missing")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData)
|
||||
let wgConfigStr = wgConfig.str
|
||||
wg_log(.info, title: "config: ", message: wgConfig.redux)
|
||||
|
||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
|
||||
|
||||
if tunnelConfiguration.peers.first!.allowedIPs
|
||||
.map({ $0.stringRepresentation })
|
||||
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
|
||||
if wgConfig.splitTunnelType == 1 {
|
||||
for index in tunnelConfiguration.peers.indices {
|
||||
tunnelConfiguration.peers[index].allowedIPs.removeAll()
|
||||
var allowedIPs = [IPAddressRange]()
|
||||
|
||||
for allowedIPString in wgConfig.splitTunnelSites {
|
||||
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||
allowedIPs.append(allowedIP)
|
||||
}
|
||||
}
|
||||
|
||||
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
|
||||
}
|
||||
} else if wgConfig.splitTunnelType == 2 {
|
||||
for index in tunnelConfiguration.peers.indices {
|
||||
var excludeIPs = [IPAddressRange]()
|
||||
|
||||
for excludeIPString in wgConfig.splitTunnelSites {
|
||||
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||
excludeIPs.append(excludeIP)
|
||||
}
|
||||
}
|
||||
|
||||
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg_log(.info, message: "Starting tunnel from the " +
|
||||
(activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
||||
|
||||
// Start the tunnel
|
||||
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
||||
guard let adapterError else {
|
||||
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
||||
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
switch adapterError {
|
||||
case .cannotLocateTunnelFileDescriptor:
|
||||
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||
case .dnsResolution(let dnsErrors):
|
||||
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
||||
.joined(separator: ", ")
|
||||
wg_log(.error, message:
|
||||
"DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
||||
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
||||
case .setNetworkSettings(let error):
|
||||
wg_log(.error, message:
|
||||
"Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||
case .startWireGuardBackend(let errorCode):
|
||||
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
||||
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
||||
case .invalidState:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
wg_log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
|
||||
let components = settings!.components(separatedBy: "\n")
|
||||
|
||||
var settingsDictionary: [String: String] = [:]
|
||||
for component in components {
|
||||
let pair = component.components(separatedBy: "=")
|
||||
if pair.count == 2 {
|
||||
settingsDictionary[pair[0]] = pair[1]
|
||||
}
|
||||
}
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
}
|
||||
|
||||
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
if messageData.count == 1 && messageData[0] == 0 {
|
||||
wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
completionHandler(data)
|
||||
}
|
||||
} else if messageData.count >= 1 {
|
||||
// Updates the tunnel configuration and responds with the active configuration
|
||||
wg_log(.info, message: "Switching tunnel configuration")
|
||||
guard let configString = String(data: messageData, encoding: .utf8)
|
||||
else {
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
||||
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
||||
if let error {
|
||||
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
self.wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
completionHandler(data)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
completionHandler(nil)
|
||||
}
|
||||
} else {
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
|
||||
// dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
//
|
||||
// let emptyTunnelConfiguration = TunnelConfiguration(
|
||||
// name: nil,
|
||||
// interface: InterfaceConfiguration(privateKey: PrivateKey()),
|
||||
// peers: []
|
||||
// )
|
||||
//
|
||||
// wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
|
||||
// self.dispatchQueue.async {
|
||||
// if let error {
|
||||
// wg_log(.error, message: "Failed to start an empty tunnel")
|
||||
// completionHandler(error)
|
||||
// } else {
|
||||
// wg_log(.info, message: "Started an empty tunnel")
|
||||
// self.tunnelAdapterDidStart()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
|
||||
//
|
||||
// self.setTunnelNetworkSettings(settings) { error in
|
||||
// completionHandler(error)
|
||||
// }
|
||||
// }
|
||||
|
||||
// private func tunnelAdapterDidStart() {
|
||||
// dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
// // ...
|
||||
// }
|
||||
|
||||
func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
wg_log(.info, message: "Stopping tunnel: reason: \(reason.description)")
|
||||
|
||||
wgAdapter.stop { error in
|
||||
ErrorNotifier.removeLastErrorFile()
|
||||
|
||||
if let error {
|
||||
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
||||
}
|
||||
completionHandler()
|
||||
|
||||
#if os(macOS)
|
||||
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
||||
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
||||
// sufficient quantities of users.
|
||||
exit(0)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ struct Constants {
|
||||
static let ovpnConfigKey = "ovpn"
|
||||
static let wireGuardConfigKey = "wireguard"
|
||||
static let loggerTag = "NET"
|
||||
|
||||
|
||||
static let kActionStart = "start"
|
||||
static let kActionRestart = "restart"
|
||||
static let kActionStop = "stop"
|
||||
@@ -34,62 +34,68 @@ struct Constants {
|
||||
}
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
private lazy var wgAdapter = {
|
||||
lazy var wgAdapter = {
|
||||
WireGuardAdapter(with: self) { logLevel, message in
|
||||
wg_log(logLevel.osLogLevel, message: message)
|
||||
}
|
||||
}()
|
||||
|
||||
private lazy var ovpnAdapter: OpenVPNAdapter = {
|
||||
|
||||
lazy var ovpnAdapter: OpenVPNAdapter = {
|
||||
let adapter = OpenVPNAdapter()
|
||||
adapter.delegate = self
|
||||
return adapter
|
||||
}()
|
||||
|
||||
|
||||
/// Internal queue.
|
||||
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
||||
|
||||
private var openVPNConfig: Data?
|
||||
|
||||
var splitTunnelType: Int!
|
||||
var splitTunnelSites: [String]!
|
||||
|
||||
|
||||
let vpnReachability = OpenVPNReachability()
|
||||
|
||||
|
||||
var startHandler: ((Error?) -> Void)?
|
||||
var stopHandler: (() -> Void)?
|
||||
var protoType: TunnelProtoType = .none
|
||||
|
||||
|
||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
let tmpStr = String(data: messageData, encoding: .utf8)!
|
||||
wg_log(.error, message: tmpStr)
|
||||
guard let message = String(data: messageData, encoding: .utf8) else {
|
||||
if let completionHandler {
|
||||
completionHandler(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
neLog(.info, title: "App said: ", message: message)
|
||||
|
||||
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
|
||||
log(.error, message: "Failed to serialize message from app")
|
||||
neLog(.error, message: "Failed to serialize message from app")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let completionHandler else {
|
||||
log(.error, message: "Missing message completion handler")
|
||||
neLog(.error, message: "Missing message completion handler")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let action = message[Constants.kMessageKeyAction] as? String else {
|
||||
log(.error, message: "Missing action key in app message")
|
||||
neLog(.error, message: "Missing action key in app message")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if action == Constants.kActionStatus {
|
||||
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
dispatchQueue.async {
|
||||
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
|
||||
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
||||
|
||||
log(.info, message: "PacketTunnelProvider startTunnel")
|
||||
|
||||
|
||||
neLog(.info, message: "Start tunnel")
|
||||
|
||||
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol {
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration
|
||||
if (providerConfiguration?[Constants.ovpnConfigKey] as? Data) != nil {
|
||||
@@ -100,7 +106,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
} else {
|
||||
self.protoType = .none
|
||||
}
|
||||
|
||||
|
||||
switch self.protoType {
|
||||
case .wireguard:
|
||||
self.startWireguard(activationAttemptId: activationAttemptId,
|
||||
@@ -116,7 +122,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
dispatchQueue.async {
|
||||
switch self.protoType {
|
||||
@@ -132,7 +138,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func handleStatusAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
switch protoType {
|
||||
case .wireguard:
|
||||
@@ -146,291 +152,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private methods
|
||||
private func startWireguard(activationAttemptId: String?,
|
||||
errorNotifier: ErrorNotifier,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
||||
wg_log(.error, message: "Can't start WireGuard config missing")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData)
|
||||
let wgConfigStr = wgConfig.str
|
||||
log(.info, message: "wgConfig: \(wgConfig.redux.replacingOccurrences(of: "\n", with: " "))")
|
||||
|
||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
|
||||
|
||||
if tunnelConfiguration.peers.first!.allowedIPs
|
||||
.map({ $0.stringRepresentation })
|
||||
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
|
||||
if wgConfig.splitTunnelType == 1 {
|
||||
for index in tunnelConfiguration.peers.indices {
|
||||
tunnelConfiguration.peers[index].allowedIPs.removeAll()
|
||||
var allowedIPs = [IPAddressRange]()
|
||||
|
||||
for allowedIPString in wgConfig.splitTunnelSites {
|
||||
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||
allowedIPs.append(allowedIP)
|
||||
}
|
||||
}
|
||||
|
||||
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
|
||||
}
|
||||
} else if wgConfig.splitTunnelType == 2 {
|
||||
for index in tunnelConfiguration.peers.indices {
|
||||
var excludeIPs = [IPAddressRange]()
|
||||
|
||||
for excludeIPString in wgConfig.splitTunnelSites {
|
||||
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||
excludeIPs.append(excludeIP)
|
||||
}
|
||||
}
|
||||
|
||||
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg_log(.info, message: "Starting wireguard tunnel from the " +
|
||||
(activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
||||
|
||||
// Start the tunnel
|
||||
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
||||
guard let adapterError else {
|
||||
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
||||
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
switch adapterError {
|
||||
case .cannotLocateTunnelFileDescriptor:
|
||||
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||
case .dnsResolution(let dnsErrors):
|
||||
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
||||
.joined(separator: ", ")
|
||||
wg_log(.error, message:
|
||||
"DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
||||
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
||||
case .setNetworkSettings(let error):
|
||||
wg_log(.error, message:
|
||||
"Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||
case .startWireGuardBackend(let errorCode):
|
||||
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
||||
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
||||
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
||||
case .invalidState:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
||||
wg_log(.error, message: "Can't start startOpenVPN()")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
log(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self).replacingOccurrences(of: "\n", with: " "))")
|
||||
|
||||
let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData)
|
||||
log(.info, message: "openVPNConfig: \(openVPNConfig.str.replacingOccurrences(of: "\n", with: " "))")
|
||||
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
|
||||
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
||||
} catch {
|
||||
log(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)")
|
||||
|
||||
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
|
||||
log(.error, message: "Can't parse OpenVPN config: \(underlyingError.localizedDescription)")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
wg_log(.info, staticMessage: "Stopping tunnel")
|
||||
|
||||
wgAdapter.stop { error in
|
||||
ErrorNotifier.removeLastErrorFile()
|
||||
|
||||
if let error {
|
||||
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
||||
}
|
||||
completionHandler()
|
||||
|
||||
#if os(macOS)
|
||||
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
||||
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
||||
// sufficient quantities of users.
|
||||
exit(0)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
stopHandler = completionHandler
|
||||
if vpnReachability.isTracking {
|
||||
vpnReachability.stopTracking()
|
||||
}
|
||||
ovpnAdapter.disconnect()
|
||||
}
|
||||
|
||||
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
|
||||
let components = settings!.components(separatedBy: "\n")
|
||||
|
||||
var settingsDictionary: [String: String] = [:]
|
||||
for component in components {
|
||||
let pair = component.components(separatedBy: "=")
|
||||
if pair.count == 2 {
|
||||
settingsDictionary[pair[0]] = pair[1]
|
||||
}
|
||||
}
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
}
|
||||
|
||||
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
if messageData.count == 1 && messageData[0] == 0 {
|
||||
wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
completionHandler(data)
|
||||
}
|
||||
} else if messageData.count >= 1 {
|
||||
// Updates the tunnel configuration and responds with the active configuration
|
||||
wg_log(.info, message: "Switching tunnel configuration")
|
||||
guard let configString = String(data: messageData, encoding: .utf8)
|
||||
else {
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
||||
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
||||
if let error {
|
||||
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
self.wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
completionHandler(data)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
completionHandler(nil)
|
||||
}
|
||||
} else {
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
let bytesin = ovpnAdapter.transportStatistics.bytesIn
|
||||
let bytesout = ovpnAdapter.transportStatistics.bytesOut
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes": bytesin,
|
||||
"tx_bytes": bytesout
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
|
||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
|
||||
withShadowSocks viaSS: Bool = false,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
wg_log(.info, message: "setupAndlaunchOpenVPN")
|
||||
|
||||
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
||||
|
||||
let configuration = OpenVPNConfiguration()
|
||||
configuration.fileContent = ovpnConfiguration
|
||||
if str.contains("cloak") {
|
||||
configuration.setPTCloak()
|
||||
}
|
||||
|
||||
let evaluation: OpenVPNConfigurationEvaluation
|
||||
do {
|
||||
evaluation = try ovpnAdapter.apply(configuration: configuration)
|
||||
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
|
||||
if !evaluation.autologin {
|
||||
wg_log(.info, message: "Implement login with user credentials")
|
||||
}
|
||||
|
||||
vpnReachability.startTracking { [weak self] status in
|
||||
guard status == .reachableViaWiFi else { return }
|
||||
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
|
||||
}
|
||||
|
||||
startHandler = completionHandler
|
||||
ovpnAdapter.connect(using: packetFlow)
|
||||
|
||||
// let ifaces = Interface.allInterfaces()
|
||||
// .filter { $0.family == .ipv4 }
|
||||
// .map { iface in iface.name }
|
||||
|
||||
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Network observing methods
|
||||
|
||||
|
||||
private func startListeningForNetworkChanges() {
|
||||
stopListeningForNetworkChanges()
|
||||
addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil)
|
||||
}
|
||||
|
||||
|
||||
private func stopListeningForNetworkChanges() {
|
||||
removeObserver(self, forKeyPath: Constants.kDefaultPathKey)
|
||||
}
|
||||
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?,
|
||||
of object: Any?,
|
||||
change: [NSKeyValueChangeKey: Any]?,
|
||||
@@ -450,48 +183,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
self.handle(networkChange: self.defaultPath!) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) {
|
||||
wg_log(.info, message: "Tunnel restarted.")
|
||||
startTunnel(options: nil, completionHandler: completion)
|
||||
}
|
||||
|
||||
private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
|
||||
dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
|
||||
let emptyTunnelConfiguration = TunnelConfiguration(
|
||||
name: nil,
|
||||
interface: InterfaceConfiguration(privateKey: PrivateKey()),
|
||||
peers: []
|
||||
)
|
||||
|
||||
wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
|
||||
self.dispatchQueue.async {
|
||||
if let error {
|
||||
log(.error, message: "Failed to start an empty tunnel")
|
||||
completionHandler(error)
|
||||
} else {
|
||||
log(.info, message: "Started an empty tunnel")
|
||||
self.tunnelAdapterDidStart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
|
||||
|
||||
self.setTunnelNetworkSettings(settings) { error in
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func tunnelAdapterDidStart() {
|
||||
dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
|
||||
|
||||
extension WireGuardLogLevel {
|
||||
var osLogLevel: OSLogType {
|
||||
switch self {
|
||||
@@ -502,3 +200,46 @@ extension WireGuardLogLevel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NEProviderStopReason: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .none:
|
||||
return "No specific reason"
|
||||
case .userInitiated:
|
||||
return "The user stopped the NE"
|
||||
case .providerFailed:
|
||||
return "The NE failed to function correctly"
|
||||
case .noNetworkAvailable:
|
||||
return "No network connectivity is currently available"
|
||||
case .unrecoverableNetworkChange:
|
||||
return "The device’s network connectivity changed"
|
||||
case .providerDisabled:
|
||||
return "The NE was disabled"
|
||||
case .authenticationCanceled:
|
||||
return "The authentication process was canceled"
|
||||
case .configurationFailed:
|
||||
return "The VPNC is invalid"
|
||||
case .idleTimeout:
|
||||
return "The session timed out"
|
||||
case .configurationDisabled:
|
||||
return "The VPNC was disabled"
|
||||
case .configurationRemoved:
|
||||
return "The VPNC was removed"
|
||||
case .superceded:
|
||||
return "VPNC was superceded by a higher-priority VPNC"
|
||||
case .userLogout:
|
||||
return "The user logged out"
|
||||
case .userSwitch:
|
||||
return "The current console user changed"
|
||||
case .connectionFailed:
|
||||
return "The connection failed"
|
||||
case .sleep:
|
||||
return "A stop reason indicating the VPNC enabled disconnect on sleep and the device went to sleep"
|
||||
case .appUpdate:
|
||||
return "appUpdat"
|
||||
@unknown default:
|
||||
return "@unknown default"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
public func removeVPNC(_ vpncName: std.string) {
|
||||
let vpncName = String(describing: vpncName)
|
||||
|
||||
Task {
|
||||
await getManagers()?.first { manager in
|
||||
if let name = manager.localizedDescription, name == vpncName {
|
||||
Task {
|
||||
await remove(manager)
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func clearSettings() {
|
||||
Task {
|
||||
await getManagers()?.forEach { manager in
|
||||
Task {
|
||||
await remove(manager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getManagers() async -> [NETunnelProviderManager]? {
|
||||
do {
|
||||
return try await NETunnelProviderManager.loadAllFromPreferences()
|
||||
} catch {
|
||||
log(.error, title: "VPNC: ", message: "loadAllFromPreferences error: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func remove(_ manager: NETunnelProviderManager) async {
|
||||
let vpncName = manager.localizedDescription ?? "Unknown"
|
||||
do {
|
||||
try await manager.removeFromPreferences()
|
||||
try await manager.loadFromPreferences()
|
||||
log(.info, title: "VPNC: ", message: "Remove \(vpncName)")
|
||||
} catch {
|
||||
log(.error, title: "VPNC: ", message: "Failed to remove \(vpncName) (\(error.localizedDescription))")
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ struct WGConfig: Decodable {
|
||||
let initPacketJunkSize, responsePacketJunkSize: String?
|
||||
let dns1: String
|
||||
let dns2: String
|
||||
let mtu: String
|
||||
let hostName: String
|
||||
let port: Int
|
||||
let clientIP: String
|
||||
@@ -25,6 +26,7 @@ struct WGConfig: Decodable {
|
||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
||||
case dns1
|
||||
case dns2
|
||||
case mtu
|
||||
case hostName
|
||||
case port
|
||||
case clientIP = "client_ip"
|
||||
@@ -58,6 +60,7 @@ struct WGConfig: Decodable {
|
||||
[Interface]
|
||||
Address = \(clientIP)
|
||||
DNS = \(dns1), \(dns2)
|
||||
MTU = \(mtu)
|
||||
PrivateKey = \(clientPrivateKey)
|
||||
\(settings)
|
||||
[Peer]
|
||||
@@ -74,6 +77,7 @@ struct WGConfig: Decodable {
|
||||
[Interface]
|
||||
Address = \(clientIP)
|
||||
DNS = \(dns1), \(dns2)
|
||||
MTU = \(mtu)
|
||||
PrivateKey = ***
|
||||
\(settings)
|
||||
[Peer]
|
||||
@@ -82,16 +86,9 @@ struct WGConfig: Decodable {
|
||||
AllowedIPs = \(allowedIPs.joined(separator: ", "))
|
||||
Endpoint = \(hostName):\(port)
|
||||
PersistentKeepalive = \(persistentKeepAlive)
|
||||
|
||||
SplitTunnelType = \(splitTunnelType)
|
||||
SplitTunnelSites = \(splitTunnelSites.joined(separator: ", "))
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
struct OpenVPNConfig: Decodable {
|
||||
let config: String
|
||||
let splitTunnelType: Int
|
||||
let splitTunnelSites: [String]
|
||||
|
||||
var str: String {
|
||||
"splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,9 +89,11 @@ bool IosController::initialize()
|
||||
|
||||
|
||||
for (NETunnelProviderManager *manager in managers) {
|
||||
qDebug() << "IosController::initialize : VPNC: " << manager.localizedDescription;
|
||||
|
||||
if (manager.connection.status == NEVPNStatusConnected) {
|
||||
m_currentTunnel = manager;
|
||||
qDebug() << "IosController::initialize : VPN already connected";
|
||||
qDebug() << "IosController::initialize : VPN already connected with" << manager.localizedDescription;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
break;
|
||||
|
||||
@@ -138,7 +140,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
||||
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
|
||||
@try {
|
||||
if (error) {
|
||||
qDebug() << "IosController::connectVpn : Error:" << [error.localizedDescription UTF8String];
|
||||
qDebug() << "IosController::connectVpn : VPNC: loadAllFromPreferences error:" << [error.localizedDescription UTF8String];
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
ok = false;
|
||||
return;
|
||||
@@ -151,7 +153,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
||||
for (NETunnelProviderManager *manager in managers) {
|
||||
if ([manager.localizedDescription isEqualToString:tunnelName.toNSString()]) {
|
||||
m_currentTunnel = manager;
|
||||
qDebug() << "IosController::connectVpn : Using existing tunnel";
|
||||
qDebug() << "IosController::connectVpn : Using existing tunnel:" << manager.localizedDescription;
|
||||
if (manager.connection.status == NEVPNStatusConnected) {
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
return;
|
||||
@@ -162,10 +164,10 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
||||
}
|
||||
|
||||
if (!m_currentTunnel) {
|
||||
qDebug() << "IosController::connectVpn : Creating new tunnel";
|
||||
isNewTunnelCreated = true;
|
||||
m_currentTunnel = [[NETunnelProviderManager alloc] init];
|
||||
m_currentTunnel.localizedDescription = [NSString stringWithUTF8String:tunnelName.toStdString().c_str()];
|
||||
qDebug() << "IosController::connectVpn : Creating new tunnel" << m_currentTunnel.localizedDescription;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -358,6 +360,13 @@ bool IosController::setupOpenVPN()
|
||||
|
||||
QJsonObject openVPNConfig {};
|
||||
openVPNConfig.insert(config_key::config, ovpnConfig);
|
||||
|
||||
if (ovpn.contains(config_key::mtu)) {
|
||||
openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]);
|
||||
} else {
|
||||
openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu);
|
||||
}
|
||||
|
||||
openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||
|
||||
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||
@@ -410,6 +419,13 @@ bool IosController::setupCloak()
|
||||
|
||||
QJsonObject openVPNConfig {};
|
||||
openVPNConfig.insert(config_key::config, ovpnConfig);
|
||||
|
||||
if (ovpn.contains(config_key::mtu)) {
|
||||
openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]);
|
||||
} else {
|
||||
openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu);
|
||||
}
|
||||
|
||||
openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||
|
||||
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||
@@ -433,6 +449,13 @@ bool IosController::setupWireGuard()
|
||||
QJsonObject wgConfig {};
|
||||
wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]);
|
||||
wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]);
|
||||
|
||||
if (config.contains(config_key::mtu)) {
|
||||
wgConfig.insert(config_key::mtu, config[config_key::mtu]);
|
||||
} else {
|
||||
wgConfig.insert(config_key::mtu, protocols::wireguard::defaultMtu);
|
||||
}
|
||||
|
||||
wgConfig.insert(config_key::hostName, config[config_key::hostName]);
|
||||
wgConfig.insert(config_key::port, config[config_key::port]);
|
||||
wgConfig.insert(config_key::client_ip, config[config_key::client_ip]);
|
||||
@@ -449,21 +472,18 @@ bool IosController::setupWireGuard()
|
||||
|
||||
wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||
|
||||
if (config.contains(config_key::allowed_ips)) {
|
||||
QJsonArray allowed_ips;
|
||||
QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", ");
|
||||
|
||||
for(int index = 0; index < allowed_ips_list.length(); index++) {
|
||||
allowed_ips.append(allowed_ips_list[index]);
|
||||
}
|
||||
|
||||
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||
if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) {
|
||||
wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]);
|
||||
} else {
|
||||
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
|
||||
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||
}
|
||||
|
||||
wgConfig.insert("persistent_keep_alive", "25");
|
||||
if (config.contains(config_key::persistent_keep_alive)) {
|
||||
wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]);
|
||||
} else {
|
||||
wgConfig.insert(config_key::persistent_keep_alive, "25");
|
||||
}
|
||||
|
||||
QJsonDocument wgConfigDoc(wgConfig);
|
||||
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||
@@ -478,6 +498,13 @@ bool IosController::setupAwg()
|
||||
QJsonObject wgConfig {};
|
||||
wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]);
|
||||
wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]);
|
||||
|
||||
if (config.contains(config_key::mtu)) {
|
||||
wgConfig.insert(config_key::mtu, config[config_key::mtu]);
|
||||
} else {
|
||||
wgConfig.insert(config_key::mtu, protocols::awg::defaultMtu);
|
||||
}
|
||||
|
||||
wgConfig.insert(config_key::hostName, config[config_key::hostName]);
|
||||
wgConfig.insert(config_key::port, config[config_key::port]);
|
||||
wgConfig.insert(config_key::client_ip, config[config_key::client_ip]);
|
||||
@@ -494,21 +521,19 @@ bool IosController::setupAwg()
|
||||
|
||||
wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||
|
||||
if (config.contains(config_key::allowed_ips)) {
|
||||
QJsonArray allowed_ips;
|
||||
QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", ");
|
||||
|
||||
for(int index = 0; index < allowed_ips_list.length(); index++) {
|
||||
allowed_ips.append(allowed_ips_list[index]);
|
||||
}
|
||||
|
||||
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||
if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) {
|
||||
wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]);
|
||||
} else {
|
||||
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
|
||||
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||
}
|
||||
|
||||
wgConfig.insert("persistent_keep_alive", "25");
|
||||
if (config.contains(config_key::persistent_keep_alive)) {
|
||||
wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]);
|
||||
} else {
|
||||
wgConfig.insert(config_key::persistent_keep_alive, "25");
|
||||
}
|
||||
|
||||
wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]);
|
||||
wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]);
|
||||
wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]);
|
||||
@@ -575,13 +600,14 @@ void IosController::startTunnel()
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
if (saveError) {
|
||||
qDebug().nospace() << "IosController::startTunnel" << protocolName << ": Connect " << protocolName << " Tunnel Save Error" << saveError.localizedDescription.UTF8String;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
[m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) {
|
||||
if (loadError) {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String;
|
||||
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
@@ -592,11 +618,11 @@ void IosController::startTunnel()
|
||||
BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError];
|
||||
|
||||
if (!started || startError) {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << " : Connect " << protocolName << " Tunnel Start Error"
|
||||
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Connect " << protocolName << " Tunnel Start Error"
|
||||
<< (startError ? startError.localizedDescription.UTF8String : "");
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
} else {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << " : Starting the tunnel succeeded";
|
||||
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Starting the tunnel succeeded";
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
@@ -15,7 +15,6 @@ class IOSNetworkWatcher : public NetworkWatcherImpl {
|
||||
~IOSNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
NetworkWatcherImpl::TransportType getTransportType() override;
|
||||
|
||||
private:
|
||||
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);
|
||||
|
||||
@@ -37,16 +37,6 @@ void IOSNetworkWatcher::initialize() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::getTransportType() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
if (m_observableConnection != nil) {
|
||||
return m_currentVPNTransport;
|
||||
}
|
||||
// If we don't have an open tunnel-observer, m_currentVPNTransport is probably wrong.
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) {
|
||||
if (path == nil) {
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr uint32_t ETH_MTU = 1500;
|
||||
constexpr uint32_t WG_MTU_OVERHEAD = 80;
|
||||
|
||||
namespace {
|
||||
Logger logger("IPUtilsLinux");
|
||||
}
|
||||
@@ -38,8 +35,6 @@ bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) {
|
||||
}
|
||||
|
||||
bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
|
||||
// Create socket file descriptor to perform the ioctl operations on
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sockfd < 0) {
|
||||
@@ -56,10 +51,10 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
|
||||
// FIXME: We need to know how many layers deep this particular
|
||||
// interface is into a tunnel to work effectively. Otherwise
|
||||
// we will run into fragmentation issues.
|
||||
ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD;
|
||||
ifr.ifr_mtu = config.m_deviceMTU;
|
||||
int ret = ioctl(sockfd, SIOCSIFMTU, &ifr);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set MTU -- Return code: " << ret;
|
||||
logger.error() << "Failed to set MTU -- " << config.m_deviceMTU << " -- Return code: " << ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../utilities.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("LinuxRouteMonitor");
|
||||
@@ -163,7 +165,7 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
|
||||
|
||||
if (rtm->rtm_type == RTN_THROW) {
|
||||
struct in_addr ip4;
|
||||
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4);
|
||||
inet_pton(AF_INET, NetworkUtilities::getGatewayAndIface().toUtf8(), &ip4);
|
||||
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
|
||||
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
|
||||
rtm->rtm_type = RTN_UNICAST;
|
||||
@@ -221,122 +223,6 @@ void LinuxRouteMonitor::nlsockReady() {
|
||||
}
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
QString LinuxRouteMonitor::getgatewayandiface()
|
||||
{
|
||||
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
|
||||
int sock = -1, msgseq = 0;
|
||||
struct nlmsghdr *nlh, *nlmsg;
|
||||
struct rtmsg *route_entry;
|
||||
// This struct contain route attributes (route type)
|
||||
struct rtattr *route_attribute;
|
||||
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
|
||||
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
|
||||
char *ptr = buffer;
|
||||
struct timeval tv;
|
||||
|
||||
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
perror("socket failed");
|
||||
return "";
|
||||
}
|
||||
|
||||
memset(msgbuf, 0, sizeof(msgbuf));
|
||||
memset(gateway_address, 0, sizeof(gateway_address));
|
||||
memset(interface, 0, sizeof(interface));
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
/* point the header and the msg structure pointers into the buffer */
|
||||
nlmsg = (struct nlmsghdr *)msgbuf;
|
||||
|
||||
/* Fill in the nlmsg header*/
|
||||
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
|
||||
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
|
||||
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
|
||||
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
|
||||
|
||||
/* 1 Sec Timeout to avoid stall */
|
||||
tv.tv_sec = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||
/* send msg */
|
||||
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
|
||||
perror("send failed");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* receive response */
|
||||
do
|
||||
{
|
||||
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
|
||||
if (received_bytes < 0) {
|
||||
perror("Error in recv");
|
||||
return "";
|
||||
}
|
||||
|
||||
nlh = (struct nlmsghdr *) ptr;
|
||||
|
||||
/* Check if the header is valid */
|
||||
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
|
||||
(nlmsg->nlmsg_type == NLMSG_ERROR))
|
||||
{
|
||||
perror("Error in received packet");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* If we received all data break */
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
else {
|
||||
ptr += received_bytes;
|
||||
msg_len += received_bytes;
|
||||
}
|
||||
|
||||
/* Break if its not a multi part message */
|
||||
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
|
||||
break;
|
||||
}
|
||||
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
|
||||
|
||||
/* parse response */
|
||||
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
|
||||
{
|
||||
/* Get the route data */
|
||||
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
|
||||
|
||||
/* We are just interested in main routing table */
|
||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
|
||||
route_attribute_len = RTM_PAYLOAD(nlh);
|
||||
|
||||
/* Loop through all attributes */
|
||||
for ( ; RTA_OK(route_attribute, route_attribute_len);
|
||||
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
|
||||
{
|
||||
switch(route_attribute->rta_type) {
|
||||
case RTA_OIF:
|
||||
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
inet_ntop(AF_INET, RTA_DATA(route_attribute),
|
||||
gateway_address, sizeof(gateway_address));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*gateway_address) && (*interface)) {
|
||||
logger.debug() << "Gateway " << gateway_address << " for interface " << interface;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
return gateway_address;
|
||||
}
|
||||
|
||||
static bool buildAllowedIp(wg_allowedip* ip,
|
||||
const IPAddress& prefix) {
|
||||
const char* addrString = qPrintable(prefix.address().toString());
|
||||
|
||||
@@ -31,7 +31,6 @@ class LinuxRouteMonitor final : public QObject {
|
||||
static QString addrToString(const QByteArray& data);
|
||||
bool rtmSendRoute(int action, int flags, int type,
|
||||
const IPAddress& prefix);
|
||||
QString getgatewayandiface();
|
||||
QString m_ifname;
|
||||
unsigned int m_ifindex = 0;
|
||||
int m_nlsock = -1;
|
||||
|
||||
@@ -103,15 +103,32 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||
out << "replace_peers=true\n";
|
||||
|
||||
if (config.m_junkPacketCount != "") {
|
||||
|
||||
if (!config.m_junkPacketCount.isEmpty()) {
|
||||
out << "jc=" << config.m_junkPacketCount << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMinSize.isEmpty()) {
|
||||
out << "jmin=" << config.m_junkPacketMinSize << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMaxSize.isEmpty()) {
|
||||
out << "jmax=" << config.m_junkPacketMaxSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketJunkSize.isEmpty()) {
|
||||
out << "s1=" << config.m_initPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketJunkSize.isEmpty()) {
|
||||
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketMagicHeader.isEmpty()) {
|
||||
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketMagicHeader.isEmpty()) {
|
||||
out << "h2=" << config.m_responsePacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_underloadPacketMagicHeader.isEmpty()) {
|
||||
out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketMagicHeader.isEmpty()) {
|
||||
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||
}
|
||||
|
||||
@@ -119,24 +136,25 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
} else {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
return (err == 0);
|
||||
@@ -182,7 +200,9 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
}
|
||||
if (!config.m_serverIpv4AddrIn.isNull()) {
|
||||
out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
|
||||
} else if (!config.m_serverIpv6AddrIn.isNull()) {
|
||||
|
||||
Regular → Executable
-5
@@ -22,11 +22,6 @@ class LinuxNetworkWatcher final : public NetworkWatcherImpl {
|
||||
|
||||
void start() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() {
|
||||
// TODO: Find out how to do that on linux generally. (VPN-2382)
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
};
|
||||
|
||||
signals:
|
||||
void checkDevicesInThread();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user