Compare commits

..

1 Commits

Author SHA1 Message Date
Vladyslav Miachkov dad2dc0ab0 Hide error on clear server button 2024-04-06 23:07:46 +03:00
238 changed files with 2063 additions and 12559 deletions
+1 -1
View File
@@ -233,7 +233,7 @@ jobs:
- name: 'Setup xcode' - name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1 uses: maxim-lobanov/setup-xcode@v1
with: with:
xcode-version: '14.3.1' xcode-version: '13.4'
- name: 'Install Qt' - name: 'Install Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.5.3.0 project(${PROJECT} VERSION 4.5.0.0
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 52) set(APP_ANDROID_VERSION_CODE 50)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+1 -1
View File
@@ -72,7 +72,7 @@ namespace QSimpleCrypto
/// \param notAfter - X509 end date. /// \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. /// \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(RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData, X509* generateSelfSignedCertificate(const RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
const QByteArray& certificateFileName = "", const EVP_MD* md = EVP_sha512(), const QByteArray& certificateFileName = "", const EVP_MD* md = EVP_sha512(),
const long& serialNumber = 1, const long& version = x509LastVersion, const long& serialNumber = 1, const long& version = x509LastVersion,
const long& notBefore = 0, const long& notAfter = oneYear); const long& notBefore = 0, const long& notAfter = oneYear);
+1 -1
View File
@@ -139,7 +139,7 @@ X509* QSimpleCrypto::QX509::verifyCertificate(X509* x509, X509_STORE* store)
/// \param notAfter - X509 end date. /// \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. /// \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(RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData, X509* QSimpleCrypto::QX509::generateSelfSignedCertificate(const RSA* rsa, const QMap<QByteArray, QByteArray>& additionalData,
const QByteArray& certificateFileName, const EVP_MD* md, const QByteArray& certificateFileName, const EVP_MD* md,
const long& serialNumber, const long& version, const long& serialNumber, const long& version,
const long& notBefore, const long& notAfter) const long& notBefore, const long& notAfter)
+4 -14
View File
@@ -69,8 +69,6 @@ set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar_EG.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_my_MM.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_uk_UA.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) file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
@@ -127,6 +125,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h ${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h ${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
@@ -151,16 +150,11 @@ include_directories(mozilla/models)
if(NOT IOS) if(NOT IOS)
set(HEADERS ${HEADERS} set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.h
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
) )
endif() endif()
if(NOT ANDROID)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
)
endif()
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/migrations.cpp ${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
@@ -172,6 +166,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.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}/ui/qautostart.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
@@ -192,16 +187,11 @@ endif()
if(NOT IOS) if(NOT IOS)
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.cpp
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
) )
endif() endif()
if(NOT ANDROID)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
)
endif()
file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h) file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h)
file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp) file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
+45 -40
View File
@@ -9,16 +9,17 @@
#include <QTextDocument> #include <QTextDocument>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include <QQuickItem> #include <QQuickItem>
#include "logger.h" #include "logger.h"
#include "ui/models/installedAppsModel.h"
#include "version.h" #include "version.h"
#include "ui/models/installedAppsModel.h"
#include "platforms/ios/QRCodeReaderBase.h" #include "platforms/ios/QRCodeReaderBase.h"
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
#include "core/installedAppsImageProvider.h"
#include "platforms/android/android_controller.h" #include "platforms/android/android_controller.h"
#include "core/installedAppsImageProvider.h"
#endif #endif
#include "protocols/qml_register_protocols.h" #include "protocols/qml_register_protocols.h"
@@ -31,8 +32,8 @@
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv) AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
#else #else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, int timeout, AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
const QString &userData) int timeout, const QString &userData)
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData) : SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif #endif
{ {
@@ -45,17 +46,16 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecond
s.setValue("permFixed", true); s.setValue("permFixed", true);
} }
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + ORGANIZATION_NAME + "/" QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ APPLICATION_NAME + ".conf"; + ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner); QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + ORGANIZATION_NAME + "/" QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf"; + ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner); QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif #endif
m_settings = std::shared_ptr<Settings>(new Settings); m_settings = std::shared_ptr<Settings>(new Settings);
m_nam = new QNetworkAccessManager(this);
} }
AmneziaApplication::~AmneziaApplication() AmneziaApplication::~AmneziaApplication()
@@ -100,17 +100,20 @@ void AmneziaApplication::init()
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()); 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) { connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
m_connectionController->onConnectionStateChanged(state); [this](Vpn::ConnectionState state) {
if (m_vpnConnection) m_connectionController->onConnectionStateChanged(state);
m_vpnConnection->restoreConnection(); if (m_vpnConnection)
}); m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) { if (!AndroidController::instance()->initialize()) {
qFatal("Android controller initialization failed"); qFatal("Android controller initialization failed");
} }
@@ -124,6 +127,8 @@ void AmneziaApplication::init()
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider); m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
#endif #endif
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->initialize(); IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
@@ -140,22 +145,23 @@ void AmneziaApplication::init()
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 #endif
#ifndef Q_OS_ANDROID
m_notificationHandler.reset(NotificationHandler::create(nullptr)); m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(), connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState); &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(), connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection)); &ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection); &ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated); connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
#endif &NotificationHandler::onTranslationsUpdated);
m_engine->load(url); m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
@@ -306,7 +312,8 @@ void AmneziaApplication::initModels()
m_serversModel.reset(new ServersModel(m_settings, this)); m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); 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(), connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel); &ContainersModel::updateModel);
m_serversModel->resetModel(); m_serversModel->resetModel();
@@ -359,29 +366,26 @@ void AmneziaApplication::initModels()
void AmneziaApplication::initControllers() void AmneziaApplication::initControllers()
{ {
m_connectionController.reset( m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel,
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings)); m_vpnConnection, m_settings));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(m_connectionController.get(), qOverload<const QString &>(&ConnectionController::connectionErrorOccurred), this, [this](const QString &errorMessage) { connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this,
emit m_pageController->showErrorMessage(errorMessage); [this](const QString &errorMessage) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); emit m_pageController->showErrorMessage(errorMessage);
}); emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_connectionController.get(), qOverload<ErrorCode>(&ConnectionController::connectionErrorOccurred), this, [this](ErrorCode errorCode) {
emit m_pageController->showErrorMessage(errorCode);
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(), connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
&ConnectionController::toggleConnection, Qt::QueuedConnection); &ConnectionController::toggleConnection, Qt::QueuedConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated); connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
&ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings, m_languageModel)); m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, 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()); m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(), connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer); &PageController::showPassphraseRequestDrawer);
@@ -397,12 +401,13 @@ void AmneziaApplication::initControllers()
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset( m_settingsController.reset(
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings)); new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); 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_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
+4 -9
View File
@@ -2,7 +2,6 @@
#define AMNEZIA_APPLICATION_H #define AMNEZIA_APPLICATION_H
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QNetworkAccessManager>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QThread> #include <QThread>
@@ -15,6 +14,8 @@
#include "settings.h" #include "settings.h"
#include "vpnconnection.h" #include "vpnconnection.h"
#include "core/controllers/apiController.h"
#include "ui/controllers/connectionController.h" #include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h" #include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h" #include "ui/controllers/importController.h"
@@ -27,9 +28,7 @@
#include "ui/models/containers_model.h" #include "ui/models/containers_model.h"
#include "ui/models/languageModel.h" #include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h" #include "ui/models/protocols/cloakConfigModel.h"
#ifndef Q_OS_ANDROID #include "ui/notificationhandler.h"
#include "ui/notificationhandler.h"
#endif
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h" #include "ui/models/protocols/ikev2ConfigModel.h"
#endif #endif
@@ -76,7 +75,6 @@ public:
bool parseCommands(); bool parseCommands();
QQmlApplicationEngine *qmlEngine() const; QQmlApplicationEngine *qmlEngine() const;
QNetworkAccessManager *manager() { return m_nam; }
signals: signals:
void translationsUpdated(); void translationsUpdated();
@@ -117,9 +115,7 @@ private:
QSharedPointer<VpnConnection> m_vpnConnection; QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread; QThread m_vpnConnectionThread;
#ifndef Q_OS_ANDROID
QScopedPointer<NotificationHandler> m_notificationHandler; QScopedPointer<NotificationHandler> m_notificationHandler;
#endif
QScopedPointer<ConnectionController> m_connectionController; QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<PageController> m_pageController; QScopedPointer<PageController> m_pageController;
@@ -129,9 +125,8 @@ private:
QScopedPointer<SettingsController> m_settingsController; QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController; QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController; QScopedPointer<SystemController> m_systemController;
QScopedPointer<ApiController> m_apiController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController; QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QNetworkAccessManager *m_nam;
}; };
#endif // AMNEZIA_APPLICATION_H #endif // AMNEZIA_APPLICATION_H
@@ -3,6 +3,9 @@ package org.amnezia.vpn.protocol.cloak
import android.util.Base64 import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.amnezia.vpn.protocol.openvpn.OpenVpnConfig
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject import org.json.JSONObject
/** /**
@@ -51,6 +54,13 @@ class Cloak : OpenVpn() {
return openVpnConfig return openVpnConfig
} }
override fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {
// exclude remote server ip from vpn routes
val remoteServer = config.getString("hostName")
val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer))
configBuilder.excludeRoute(remoteServerAddress)
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject { private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
cloakConfigJson.put("NumConn", 1) cloakConfigJson.put("NumConn", 1)
cloakConfigJson.put("ProxyMethod", "openvpn") cloakConfigJson.put("ProxyMethod", "openvpn")
@@ -13,9 +13,7 @@ import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.getLocalNetworks import org.amnezia.vpn.util.net.getLocalNetworks
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject import org.json.JSONObject
/** /**
@@ -79,12 +77,6 @@ open class OpenVpn : Protocol() {
if (evalConfig.error) { if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}") throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
} }
// exclude remote server ip from vpn routes
val remoteServer = config.getString("hostName")
val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer))
configBuilder.excludeRoute(remoteServerAddress)
configPluggableTransport(configBuilder, config) configPluggableTransport(configBuilder, config)
configBuilder.configSplitTunneling(config) configBuilder.configSplitTunneling(config)
configBuilder.configAppSplitTunneling(config) configBuilder.configAppSplitTunneling(config)
@@ -105,27 +105,25 @@ abstract class Protocol {
vpnBuilder.addSearchDomain(it) vpnBuilder.addSearchDomain(it)
} }
for ((inetNetwork, include) in config.routes) { for (addr in config.routes) {
if (include) { Log.d(TAG, "addRoute: $addr")
Log.d(TAG, "addRoute: $inetNetwork") vpnBuilder.addRoute(addr)
vpnBuilder.addRoute(inetNetwork) }
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Log.d(TAG, "excludeRoute: $inetNetwork") for (addr in config.excludedRoutes) {
vpnBuilder.excludeRoute(inetNetwork) Log.d(TAG, "excludeRoute: $addr")
} else { vpnBuilder.excludeRoute(addr)
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
}
} }
} }
for (app in config.includedApplications) { for (app in config.includedApplications) {
Log.d(TAG, "addAllowedApplication") Log.d(TAG, "addAllowedApplication: $app")
vpnBuilder.addAllowedApplication(app) vpnBuilder.addAllowedApplication(app)
} }
for (app in config.excludedApplications) { for (app in config.excludedApplications) {
Log.d(TAG, "addDisallowedApplication") Log.d(TAG, "addDisallowedApplication: $app")
vpnBuilder.addDisallowedApplication(app) vpnBuilder.addDisallowedApplication(app)
} }
@@ -12,7 +12,8 @@ open class ProtocolConfig protected constructor(
val addresses: Set<InetNetwork>, val addresses: Set<InetNetwork>,
val dnsServers: Set<InetAddress>, val dnsServers: Set<InetAddress>,
val searchDomain: String?, val searchDomain: String?,
val routes: Set<Route>, val routes: Set<InetNetwork>,
val excludedRoutes: Set<InetNetwork>,
val includedAddresses: Set<InetNetwork>, val includedAddresses: Set<InetNetwork>,
val excludedAddresses: Set<InetNetwork>, val excludedAddresses: Set<InetNetwork>,
val includedApplications: Set<String>, val includedApplications: Set<String>,
@@ -28,6 +29,7 @@ open class ProtocolConfig protected constructor(
builder.dnsServers, builder.dnsServers,
builder.searchDomain, builder.searchDomain,
builder.routes, builder.routes,
builder.excludedRoutes,
builder.includedAddresses, builder.includedAddresses,
builder.excludedAddresses, builder.excludedAddresses,
builder.includedApplications, builder.includedApplications,
@@ -41,7 +43,8 @@ open class ProtocolConfig protected constructor(
open class Builder(blockingMode: Boolean) { open class Builder(blockingMode: Boolean) {
internal val addresses: MutableSet<InetNetwork> = hashSetOf() internal val addresses: MutableSet<InetNetwork> = hashSetOf()
internal val dnsServers: MutableSet<InetAddress> = hashSetOf() internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
internal val routes: MutableSet<Route> = mutableSetOf() internal val routes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf() internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf()
internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf() internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf()
internal val includedApplications: MutableSet<String> = hashSetOf() internal val includedApplications: MutableSet<String> = hashSetOf()
@@ -74,21 +77,13 @@ open class ProtocolConfig protected constructor(
fun setSearchDomain(domain: String) = apply { this.searchDomain = domain } fun setSearchDomain(domain: String) = apply { this.searchDomain = domain }
fun addRoute(route: InetNetwork) = apply { this.routes += Route(route, true) } fun addRoute(route: InetNetwork) = apply { this.routes += route }
fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes.map { Route(it, true) } } fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes }
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
fun excludeRoute(route: InetNetwork) = apply { this.routes += Route(route, false) }
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes.map { Route(it, false) } }
fun removeRoute(route: InetNetwork) = apply { this.routes.removeIf { it.inetNetwork == route } }
fun clearRoutes() = apply { this.routes.clear() } fun clearRoutes() = apply { this.routes.clear() }
fun prependRoutes(block: Builder.() -> Unit) = apply { fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
val savedRoutes = mutableListOf<Route>().apply { addAll(routes) } fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
routes.clear()
block()
routes.addAll(savedRoutes)
}
fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr } fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr }
fun includeAddresses(addresses: Collection<InetNetwork>) = apply { this.includedAddresses += addresses } fun includeAddresses(addresses: Collection<InetNetwork>) = apply { this.includedAddresses += addresses }
@@ -122,46 +117,37 @@ open class ProtocolConfig protected constructor(
// remove default routes, if any // remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0)) removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0)) removeRoute(InetNetwork("::", 0))
removeRoute(InetNetwork("2000::", 3)) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
prependRoutes { // for older versions of Android, add the default route to the excluded routes
addRoutes(includedAddresses) // to correctly build the excluded subnets list later
excludeRoute(InetNetwork("0.0.0.0", 0))
} }
addRoutes(includedAddresses)
} else if (excludedAddresses.isNotEmpty()) { } else if (excludedAddresses.isNotEmpty()) {
prependRoutes { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// default routes are required for split tunneling in newer versions of Android
addRoute(InetNetwork("0.0.0.0", 0)) addRoute(InetNetwork("0.0.0.0", 0))
addRoute(InetNetwork("2000::", 3)) addRoute(InetNetwork("::", 0))
excludeRoutes(excludedAddresses)
} }
excludeRoutes(excludedAddresses)
} }
} }
private fun processRoutes() { private fun processExcludedRoutes() {
// replace ::/0 as it may cause LAN connection issues if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && excludedRoutes.isNotEmpty()) {
val ipv6DefaultRoute = InetNetwork("::", 0) // todo: rewrite, taking into account the current routes
if (routes.removeIf { it.include && it.inetNetwork == ipv6DefaultRoute }) { // for older versions of Android, build a list of subnets without excluded routes
prependRoutes { // and add them to routes
addRoute(InetNetwork("2000::", 3))
}
}
// for older versions of Android, build a list of subnets without excluded routes
// and add them to routes
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && routes.any { !it.include }) {
val ipRangeSet = IpRangeSet() val ipRangeSet = IpRangeSet()
routes.forEach {
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
else ipRangeSet.remove(IpRange(it.inetNetwork))
}
ipRangeSet.remove(IpRange("127.0.0.0", 8)) ipRangeSet.remove(IpRange("127.0.0.0", 8))
ipRangeSet.remove(IpRange("::1", 128)) excludedRoutes.forEach {
routes.clear() ipRangeSet.remove(IpRange(it))
}
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
ipRangeSet.subnets().forEach(::addRoute) ipRangeSet.subnets().forEach(::addRoute)
} addRoute(InetNetwork("2000::", 3))
// filter ipv4 and ipv6 loopback addresses
val ipv6Loopback = InetNetwork("::1", 128)
routes.removeIf {
it.include &&
if (it.inetNetwork.isIpv4) it.inetNetwork.address.address[0] == 127.toByte()
else it.inetNetwork == ipv6Loopback
} }
} }
@@ -179,7 +165,7 @@ open class ProtocolConfig protected constructor(
protected fun configBuild() { protected fun configBuild() {
processSplitTunneling() processSplitTunneling()
processRoutes() processExcludedRoutes()
validate() validate()
} }
@@ -191,5 +177,3 @@ open class ProtocolConfig protected constructor(
Builder(blockingMode).apply(block).build() Builder(blockingMode).apply(block).build()
} }
} }
data class Route(val inetNetwork: InetNetwork, val include: Boolean)
+4 -18
View File
@@ -1,26 +1,12 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<resources> <resources>
<string name="disconnected">Не подключено</string> <string name="connecting">Подключение</string>
<string name="connected">Подключено</string> <string name="disconnecting">Отключение</string>
<string name="connecting">Подключение…</string>
<string name="disconnecting">Отключение…</string>
<string name="reconnecting">Переподключение…</string>
<string name="connect">Подключиться</string>
<string name="disconnect">Отключиться</string>
<string name="ok">ОК</string>
<string name="cancel">Отмена</string> <string name="cancel">Отмена</string>
<string name="yes">Да</string> <string name="ok">ОК</string>
<string name="no">Нет</string>
<string name="vpnGranted">VPN-подключение разрешено</string> <string name="vpnGranted">VPN-подключение разрешено</string>
<string name="vpnDenied">VPN-подключение запрещено</string>
<string name="vpnSetupFailed">Ошибка настройки VPN</string> <string name="vpnSetupFailed">Ошибка настройки VPN</string>
<string name="vpnSetupFailedMessage">Чтобы подключиться к AmneziaVPN необходимо:\n\n- Разрешить приложению подключаться к сети VPN\n- Отключить функцию \"Постоянная VPN\" для всех остальных VPN-приложений в системных настройках VPN</string> <string name="vpnSetupFailedMessage">Чтобы подключиться к AmneziaVPN необходимо:\n\n- Разрешить приложению подключаться к сети VPN\n- Отключить функцию \"Постоянная VPN\" для всех остальных VPN-приложений в системных настройках VPN</string>
<string name="openVpnSettings">Открыть настройки VPN</string> <string name="openVpnSettings">Открыть настройки VPN</string>
<string name="notificationChannelDescription">Уведомления сервиса AmneziaVPN</string>
<string name="notificationDialogTitle">Сервис AmneziaVPN</string>
<string name="notificationDialogMessage">Показывать статус VPN в строке состояния?</string>
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
</resources> </resources>
+4 -18
View File
@@ -1,26 +1,12 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<resources> <resources>
<string name="disconnected">Not connected</string> <string name="connecting">Connecting</string>
<string name="connected">Connected</string> <string name="disconnecting">Disconnecting</string>
<string name="connecting">Connecting…</string>
<string name="disconnecting">Disconnecting…</string>
<string name="reconnecting">Reconnecting…</string>
<string name="connect">Connect</string>
<string name="disconnect">Disconnect</string>
<string name="ok">OK</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="yes">Yes</string> <string name="ok">OK</string>
<string name="no">No</string>
<string name="vpnGranted">VPN permission granted</string> <string name="vpnGranted">VPN permission granted</string>
<string name="vpnDenied">VPN permission denied</string>
<string name="vpnSetupFailed">VPN setup error</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="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> <string name="openVpnSettings">Open VPN settings</string>
<string name="notificationChannelDescription">AmneziaVPN service notification</string>
<string name="notificationDialogTitle">AmneziaVPN service</string>
<string name="notificationDialogMessage">Show the VPN state in the status bar?</string>
<string name="notificationSettingsDialogTitle">Notification settings</string>
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
<string name="openNotificationSettings">Open notification settings</string>
</resources> </resources>
@@ -1,9 +1,6 @@
package org.amnezia.vpn package org.amnezia.vpn
import android.Manifest
import android.app.AlertDialog import android.app.AlertDialog
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.Intent.EXTRA_MIME_TYPES import android.content.Intent.EXTRA_MIME_TYPES
@@ -11,8 +8,8 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
import android.content.ServiceConnection import android.content.ServiceConnection
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri
import android.net.VpnService import android.net.VpnService
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
@@ -24,7 +21,6 @@ import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import java.io.IOException import java.io.IOException
import kotlin.LazyThreadSafetyMode.NONE import kotlin.LazyThreadSafetyMode.NONE
@@ -42,7 +38,6 @@ import org.amnezia.vpn.protocol.getStatistics
import org.amnezia.vpn.protocol.getStatus import org.amnezia.vpn.protocol.getStatus
import org.amnezia.vpn.qt.QtAndroidController import org.amnezia.vpn.qt.QtAndroidController
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.qtproject.qt.android.bindings.QtActivity import org.qtproject.qt.android.bindings.QtActivity
private const val TAG = "AmneziaActivity" private const val TAG = "AmneziaActivity"
@@ -51,9 +46,6 @@ const val ACTIVITY_MESSENGER_NAME = "Activity"
private const val CHECK_VPN_PERMISSION_ACTION_CODE = 1 private const val CHECK_VPN_PERMISSION_ACTION_CODE = 1
private const val CREATE_FILE_ACTION_CODE = 2 private const val CREATE_FILE_ACTION_CODE = 2
private const val OPEN_FILE_ACTION_CODE = 3 private const val OPEN_FILE_ACTION_CODE = 3
private const val CHECK_NOTIFICATION_PERMISSION_ACTION_CODE = 4
private const val PREFS_NOTIFICATION_PERMISSION_ASKED = "NOTIFICATION_PERMISSION_ASKED"
class AmneziaActivity : QtActivity() { class AmneziaActivity : QtActivity() {
@@ -62,11 +54,8 @@ class AmneziaActivity : QtActivity() {
private var isWaitingStatus = true private var isWaitingStatus = true
private var isServiceConnected = false private var isServiceConnected = false
private var isInBoundState = false private var isInBoundState = false
private var notificationStateReceiver: BroadcastReceiver? = null
private lateinit var vpnServiceMessenger: IpcMessenger private lateinit var vpnServiceMessenger: IpcMessenger
private var tmpFileContentToSave: String = ""
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
private val vpnServiceEventHandler: Handler by lazy(NONE) { private val vpnServiceEventHandler: Handler by lazy(NONE) {
object : Handler(Looper.getMainLooper()) { object : Handler(Looper.getMainLooper()) {
@@ -146,6 +135,10 @@ class AmneziaActivity : QtActivity() {
} }
} }
private data class CheckVpnPermissionCallbacks(val onSuccess: () -> Unit, val onFail: () -> Unit)
private var checkVpnPermissionCallbacks: CheckVpnPermissionCallbacks? = null
/** /**
* Activity overloaded methods * Activity overloaded methods
*/ */
@@ -160,30 +153,9 @@ class AmneziaActivity : QtActivity() {
doBindService() doBindService()
} }
) )
registerBroadcastReceivers()
intent?.let(::processIntent) intent?.let(::processIntent)
} }
private fun registerBroadcastReceivers() {
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
arrayOf(
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
Log.d(
TAG, "Notification state changed: ${it?.action}, blocked = " +
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
)
mainScope.launch {
qtInitialized.await()
QtAndroidController.onNotificationStateChanged()
}
}
} else null
}
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent") Log.d(TAG, "onNewIntent: $intent")
@@ -221,46 +193,50 @@ class AmneziaActivity : QtActivity() {
override fun onDestroy() { override fun onDestroy() {
Log.d(TAG, "Destroy Amnezia activity") Log.d(TAG, "Destroy Amnezia activity")
unregisterBroadcastReceiver(notificationStateReceiver)
notificationStateReceiver = null
mainScope.cancel() mainScope.cancel()
super.onDestroy() super.onDestroy()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Log.d(TAG, "Process activity result, code: ${actionCodeToString(requestCode)}, " + when (requestCode) {
"resultCode: $resultCode, data: $data") CREATE_FILE_ACTION_CODE -> {
actionResultHandlers[requestCode]?.let { handler -> when (resultCode) {
when (resultCode) { RESULT_OK -> {
RESULT_OK -> handler.onSuccess(data) data?.data?.let { uri ->
else -> handler.onFail(data) alterDocument(uri)
}
}
}
} }
handler.onAny(data)
actionResultHandlers.remove(requestCode)
} ?: super.onActivityResult(requestCode, resultCode, data)
}
private fun startActivityForResult(intent: Intent, requestCode: Int, handler: ActivityResultHandler) { OPEN_FILE_ACTION_CODE -> {
actionResultHandlers[requestCode] = handler when (resultCode) {
startActivityForResult(intent, requestCode) RESULT_OK -> data?.data?.toString() ?: ""
} else -> ""
}.let { uri ->
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { QtAndroidController.onFileOpened(uri)
Log.d(TAG, "Process permission result, code: ${actionCodeToString(requestCode)}, " + }
"permissions: ${permissions.contentToString()}, results: ${grantResults.contentToString()}")
permissionRequestHandlers[requestCode]?.let { handler ->
if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) handler.onSuccess()
else handler.onFail()
} }
handler.onAny()
permissionRequestHandlers.remove(requestCode)
} ?: super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
private fun requestPermission(permission: String, requestCode: Int, handler: PermissionRequestHandler) { CHECK_VPN_PERMISSION_ACTION_CODE -> {
permissionRequestHandlers[requestCode] = handler when (resultCode) {
requestPermissions(arrayOf(permission), requestCode) RESULT_OK -> {
Log.d(TAG, "Vpn permission granted")
Toast.makeText(this, resources.getText(R.string.vpnGranted), Toast.LENGTH_LONG).show()
checkVpnPermissionCallbacks?.run { onSuccess() }
}
else -> {
Log.w(TAG, "Vpn permission denied, resultCode: $resultCode")
showOnVpnPermissionRejectDialog()
checkVpnPermissionCallbacks?.run { onFail() }
}
}
checkVpnPermissionCallbacks = null
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
} }
/** /**
@@ -292,26 +268,22 @@ class AmneziaActivity : QtActivity() {
/** /**
* Methods of starting and stopping VpnService * Methods of starting and stopping VpnService
*/ */
private fun checkVpnPermissionAndStart(vpnConfig: String) {
checkVpnPermission(
onSuccess = { startVpn(vpnConfig) },
onFail = QtAndroidController::onVpnPermissionRejected
)
}
@MainThread @MainThread
private fun checkVpnPermission(onPermissionGranted: () -> Unit) { private fun checkVpnPermission(onSuccess: () -> Unit, onFail: () -> Unit) {
Log.d(TAG, "Check VPN permission") Log.d(TAG, "Check VPN permission")
VpnService.prepare(applicationContext)?.let { intent -> VpnService.prepare(applicationContext)?.let {
startActivityForResult(intent, CHECK_VPN_PERMISSION_ACTION_CODE, ActivityResultHandler( checkVpnPermissionCallbacks = CheckVpnPermissionCallbacks(onSuccess, onFail)
onSuccess = { startActivityForResult(it, CHECK_VPN_PERMISSION_ACTION_CODE)
Log.d(TAG, "Vpn permission granted") return
Toast.makeText(this@AmneziaActivity, resources.getText(R.string.vpnGranted), Toast.LENGTH_LONG).show() }
onPermissionGranted() onSuccess()
},
onFail = {
Log.w(TAG, "Vpn permission denied")
showOnVpnPermissionRejectDialog()
mainScope.launch {
qtInitialized.await()
QtAndroidController.onVpnPermissionRejected()
}
}
))
} ?: onPermissionGranted()
} }
private fun showOnVpnPermissionRejectDialog() { private fun showOnVpnPermissionRejectDialog() {
@@ -325,44 +297,6 @@ class AmneziaActivity : QtActivity() {
.show() .show()
} }
private fun checkNotificationPermission(onChecked: () -> Unit) {
Log.d(TAG, "Check notification permission")
if (
!isNotificationPermissionGranted() &&
!Prefs.load<Boolean>(PREFS_NOTIFICATION_PERMISSION_ASKED)
) {
showNotificationPermissionDialog(onChecked)
} else {
onChecked()
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun showNotificationPermissionDialog(onChecked: () -> Unit) {
AlertDialog.Builder(this)
.setTitle(R.string.notificationDialogTitle)
.setMessage(R.string.notificationDialogMessage)
.setNegativeButton(R.string.no) { _, _ ->
Prefs.save(PREFS_NOTIFICATION_PERMISSION_ASKED, true)
onChecked()
}
.setPositiveButton(R.string.yes) { _, _ ->
val saveAsked: () -> Unit = {
Prefs.save(PREFS_NOTIFICATION_PERMISSION_ASKED, true)
}
requestPermission(
Manifest.permission.POST_NOTIFICATIONS,
CHECK_NOTIFICATION_PERMISSION_ACTION_CODE,
PermissionRequestHandler(
onSuccess = saveAsked,
onFail = saveAsked,
onAny = onChecked
)
)
}
.show()
}
@MainThread @MainThread
private fun startVpn(vpnConfig: String) { private fun startVpn(vpnConfig: String) {
if (isServiceConnected) { if (isServiceConnected) {
@@ -388,21 +322,28 @@ class AmneziaActivity : QtActivity() {
Intent(this, AmneziaVpnService::class.java).apply { Intent(this, AmneziaVpnService::class.java).apply {
putExtra(MSG_VPN_CONFIG, vpnConfig) putExtra(MSG_VPN_CONFIG, vpnConfig)
}.also { }.also {
try { ContextCompat.startForegroundService(this, it)
ContextCompat.startForegroundService(this, it)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
QtAndroidController.onServiceError()
}
} }
} }
@MainThread
private fun disconnectFromVpn() { private fun disconnectFromVpn() {
Log.d(TAG, "Disconnect from VPN") Log.d(TAG, "Disconnect from VPN")
vpnServiceMessenger.send(Action.DISCONNECT) vpnServiceMessenger.send(Action.DISCONNECT)
} }
// saving file
private fun alterDocument(uri: Uri) {
try {
contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(tmpFileContentToSave) }
}
} catch (e: IOException) {
e.printStackTrace()
}
tmpFileContentToSave = ""
}
/** /**
* Methods called by Qt * Methods called by Qt
*/ */
@@ -416,11 +357,7 @@ class AmneziaActivity : QtActivity() {
fun start(vpnConfig: String) { fun start(vpnConfig: String) {
Log.v(TAG, "Start VPN") Log.v(TAG, "Start VPN")
mainScope.launch { mainScope.launch {
checkVpnPermission { checkVpnPermissionAndStart(vpnConfig)
checkNotificationPermission {
startVpn(vpnConfig)
}
}
} }
} }
@@ -452,26 +389,14 @@ class AmneziaActivity : QtActivity() {
fun saveFile(fileName: String, data: String) { fun saveFile(fileName: String, data: String) {
Log.d(TAG, "Save file $fileName") Log.d(TAG, "Save file $fileName")
mainScope.launch { mainScope.launch {
tmpFileContentToSave = data
Intent(Intent.ACTION_CREATE_DOCUMENT).apply { Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
type = "text/*" type = "text/*"
putExtra(Intent.EXTRA_TITLE, fileName) putExtra(Intent.EXTRA_TITLE, fileName)
}.also { }.also {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler( startActivityForResult(it, CREATE_FILE_ACTION_CODE)
onSuccess = {
it?.data?.let { uri ->
Log.d(TAG, "Save file to $uri")
try {
contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) }
}
} catch (e: IOException) {
Log.e(TAG, "Failed to save file $uri: $e")
// todo: send error to Qt
}
}
}
))
} }
} }
} }
@@ -479,47 +404,42 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused") @Suppress("unused")
fun openFile(filter: String?) { fun openFile(filter: String?) {
Log.v(TAG, "Open file with filter: $filter") Log.v(TAG, "Open file with filter: $filter")
mainScope.launch {
val mimeTypes = if (!filter.isNullOrEmpty()) {
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
val mime = MimeTypeMap.getSingleton()
extensionRegex.findAll(filter).map {
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
}.toSet()
} else emptySet()
Intent(Intent.ACTION_OPEN_DOCUMENT).apply { val mimeTypes = if (!filter.isNullOrEmpty()) {
addCategory(Intent.CATEGORY_OPENABLE) val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
Log.v(TAG, "File mimyType filter: $mimeTypes") val mime = MimeTypeMap.getSingleton()
if ("*/*" in mimeTypes) { extensionRegex.findAll(filter).map {
type = "*/*" it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
} else { }.toSet()
when (mimeTypes.size) { } else emptySet()
1 -> type = mimeTypes.first()
in 2..Int.MAX_VALUE -> { Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "*/*" addCategory(Intent.CATEGORY_OPENABLE)
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray()) Log.v(TAG, "File mimyType filter: $mimeTypes")
} if ("*/*" in mimeTypes) {
type = "*/*"
} else {
when (mimeTypes.size) {
1 -> type = mimeTypes.first()
else -> type = "*/*" in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
} }
else -> type = "*/*"
} }
}.also {
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = {
val uri = it?.data?.toString() ?: ""
Log.d(TAG, "Open file: $uri")
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
))
} }
}.also {
startActivityForResult(it, OPEN_FILE_ACTION_CODE)
} }
} }
@Suppress("unused")
fun setNotificationText(title: String, message: String, timerSec: Int) {
Log.v(TAG, "Set notification text")
}
@Suppress("unused") @Suppress("unused")
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
@@ -533,7 +453,7 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused") @Suppress("unused")
fun setSaveLogs(enabled: Boolean) { fun setSaveLogs(enabled: Boolean) {
Log.v(TAG, "Set save logs: $enabled") Log.d(TAG, "Set save logs: $enabled")
mainScope.launch { mainScope.launch {
Log.saveLogs = enabled Log.saveLogs = enabled
vpnServiceMessenger.send { vpnServiceMessenger.send {
@@ -553,9 +473,7 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused") @Suppress("unused")
fun clearLogs() { fun clearLogs() {
Log.v(TAG, "Clear logs") Log.v(TAG, "Clear logs")
mainScope.launch { Log.clearLogs()
Log.clearLogs()
}
} }
@Suppress("unused") @Suppress("unused")
@@ -591,79 +509,7 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused") @Suppress("unused")
fun getAppIcon(packageName: String, width: Int, height: Int): Bitmap { fun getAppIcon(packageName: String, width: Int, height: Int): Bitmap {
Log.v(TAG, "Get app icon") Log.v(TAG, "Get app icon: $packageName")
return AppListProvider.getAppIcon(packageManager, packageName, width, height) return AppListProvider.getAppIcon(packageManager, packageName, width, height)
} }
@Suppress("unused")
fun isNotificationPermissionGranted(): Boolean = applicationContext.isNotificationPermissionGranted()
@Suppress("unused")
fun requestNotificationPermission() {
val shouldShowPreRequest = shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
requestPermission(
Manifest.permission.POST_NOTIFICATIONS,
CHECK_NOTIFICATION_PERMISSION_ACTION_CODE,
PermissionRequestHandler(
onSuccess = {
mainScope.launch {
Prefs.save(PREFS_NOTIFICATION_PERMISSION_ASKED, true)
vpnServiceMessenger.send(Action.NOTIFICATION_PERMISSION_GRANTED)
qtInitialized.await()
QtAndroidController.onNotificationStateChanged()
}
},
onFail = {
if (!Prefs.load<Boolean>(PREFS_NOTIFICATION_PERMISSION_ASKED)) {
Prefs.save(PREFS_NOTIFICATION_PERMISSION_ASKED, true)
} else {
val shouldShowPostRequest =
shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
if (!shouldShowPreRequest && !shouldShowPostRequest) {
showNotificationSettingsDialog()
}
}
}
)
)
}
private fun showNotificationSettingsDialog() {
AlertDialog.Builder(this)
.setTitle(R.string.notificationSettingsDialogTitle)
.setMessage(R.string.notificationSettingsDialogMessage)
.setNegativeButton(R.string.cancel) { _, _ -> }
.setPositiveButton(R.string.openNotificationSettings) { _, _ ->
startActivity(Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
})
}
.show()
}
/**
* Utils methods
*/
companion object {
private fun actionCodeToString(actionCode: Int): String =
when (actionCode) {
CHECK_VPN_PERMISSION_ACTION_CODE -> "CHECK_VPN_PERMISSION"
CREATE_FILE_ACTION_CODE -> "CREATE_FILE"
OPEN_FILE_ACTION_CODE -> "OPEN_FILE"
CHECK_NOTIFICATION_PERMISSION_ACTION_CODE -> "CHECK_NOTIFICATION_PERMISSION"
else -> actionCode.toString()
}
}
} }
private class ActivityResultHandler(
val onSuccess: (data: Intent?) -> Unit = {},
val onFail: (data: Intent?) -> Unit = {},
val onAny: (data: Intent?) -> Unit = {}
)
private class PermissionRequestHandler(
val onSuccess: () -> Unit = {},
val onFail: () -> Unit = {},
val onAny: () -> Unit = {}
)
@@ -3,11 +3,14 @@ package org.amnezia.vpn
import androidx.camera.camera2.Camera2Config import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraXConfig import androidx.camera.core.CameraXConfig
import androidx.core.app.NotificationChannelCompat.Builder
import androidx.core.app.NotificationManagerCompat
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs import org.amnezia.vpn.util.Prefs
import org.qtproject.qt.android.bindings.QtApplication import org.qtproject.qt.android.bindings.QtApplication
private const val TAG = "AmneziaApplication" private const val TAG = "AmneziaApplication"
const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
class AmneziaApplication : QtApplication(), CameraXConfig.Provider { class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
@@ -17,7 +20,7 @@ class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
Log.init(this) Log.init(this)
VpnStateStore.init(this) VpnStateStore.init(this)
Log.d(TAG, "Create Amnezia application") Log.d(TAG, "Create Amnezia application")
ServiceNotification.createNotificationChannel(this) createNotificationChannel()
} }
override fun getCameraXConfig(): CameraXConfig = CameraXConfig.Builder override fun getCameraXConfig(): CameraXConfig = CameraXConfig.Builder
@@ -25,4 +28,14 @@ class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
.setMinimumLoggingLevel(android.util.Log.ERROR) .setMinimumLoggingLevel(android.util.Log.ERROR)
.setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
.build() .build()
private fun createNotificationChannel() {
NotificationManagerCompat.from(this).createNotificationChannel(
Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
.setName("AmneziaVPN")
.setDescription("AmneziaVPN service notification")
.setShowBadge(false)
.build()
)
}
} }
@@ -1,56 +0,0 @@
package org.amnezia.vpn
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.RegisterReceiverFlags
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTING
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
fun Context.getString(state: ProtocolState): String =
getString(
when (state) {
DISCONNECTED, UNKNOWN -> R.string.disconnected
CONNECTED -> R.string.connected
CONNECTING -> R.string.connecting
DISCONNECTING -> R.string.disconnecting
RECONNECTING -> R.string.reconnecting
}
)
fun Context.registerBroadcastReceiver(
action: String,
@RegisterReceiverFlags flags: Int = ContextCompat.RECEIVER_EXPORTED,
onReceive: (Intent?) -> Unit
): BroadcastReceiver = registerBroadcastReceiver(arrayOf(action), flags, onReceive)
fun Context.registerBroadcastReceiver(
actions: Array<String>,
@RegisterReceiverFlags flags: Int = ContextCompat.RECEIVER_EXPORTED,
onReceive: (Intent?) -> Unit
): BroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
onReceive(intent)
}
}.also {
ContextCompat.registerReceiver(
this,
it,
IntentFilter().apply {
actions.forEach(::addAction)
},
flags
)
}
fun Context.unregisterBroadcastReceiver(receiver: BroadcastReceiver?) {
receiver?.let { this.unregisterReceiver(it) }
}
@@ -188,16 +188,11 @@ class AmneziaTileService : TileService() {
true true
} }
private fun startVpnService() { private fun startVpnService() =
try { ContextCompat.startForegroundService(
ContextCompat.startForegroundService( applicationContext,
applicationContext, Intent(this, AmneziaVpnService::class.java)
Intent(this, AmneziaVpnService::class.java) )
)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
}
}
private fun connectToVpn() = vpnServiceMessenger.send(Action.CONNECT) private fun connectToVpn() = vpnServiceMessenger.send(Action.CONNECT)
@@ -235,7 +230,7 @@ class AmneziaTileService : TileService() {
val tile = qsTile ?: return val tile = qsTile ?: return
tile.apply { tile.apply {
label = vpnState.serverName ?: DEFAULT_TILE_LABEL label = vpnState.serverName ?: DEFAULT_TILE_LABEL
when (val protocolState = vpnState.protocolState) { when (vpnState.protocolState) {
CONNECTED -> { CONNECTED -> {
state = Tile.STATE_ACTIVE state = Tile.STATE_ACTIVE
subtitleCompat = null subtitleCompat = null
@@ -246,9 +241,14 @@ class AmneziaTileService : TileService() {
subtitleCompat = null subtitleCompat = null
} }
CONNECTING, DISCONNECTING, RECONNECTING -> { CONNECTING, RECONNECTING -> {
state = Tile.STATE_UNAVAILABLE state = Tile.STATE_UNAVAILABLE
subtitleCompat = getString(protocolState) subtitleCompat = resources.getString(R.string.connecting)
}
DISCONNECTING -> {
state = Tile.STATE_UNAVAILABLE
subtitleCompat = resources.getString(R.string.disconnecting)
} }
} }
updateTile() updateTile()
@@ -2,8 +2,8 @@ package org.amnezia.vpn
import android.app.ActivityManager import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.NotificationManager import android.app.Notification
import android.content.BroadcastReceiver import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
@@ -15,12 +15,10 @@ import android.os.IBinder
import android.os.Looper import android.os.Looper
import android.os.Message import android.os.Message
import android.os.Messenger import android.os.Messenger
import android.os.PowerManager
import android.os.Process import android.os.Process
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.LazyThreadSafetyMode.NONE import kotlin.LazyThreadSafetyMode.NONE
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@@ -56,14 +54,11 @@ import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs import org.amnezia.vpn.util.Prefs
import org.amnezia.vpn.util.net.NetworkState import org.amnezia.vpn.util.net.NetworkState
import org.amnezia.vpn.util.net.TrafficStats
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
private const val TAG = "AmneziaVpnService" private const val TAG = "AmneziaVpnService"
const val ACTION_DISCONNECT = "org.amnezia.vpn.action.disconnect"
const val MSG_VPN_CONFIG = "VPN_CONFIG" const val MSG_VPN_CONFIG = "VPN_CONFIG"
const val MSG_ERROR = "ERROR" const val MSG_ERROR = "ERROR"
const val MSG_SAVE_LOGS = "SAVE_LOGS" const val MSG_SAVE_LOGS = "SAVE_LOGS"
@@ -74,8 +69,8 @@ private const val PREFS_CONFIG_KEY = "LAST_CONF"
private const val PREFS_SERVER_NAME = "LAST_SERVER_NAME" private const val PREFS_SERVER_NAME = "LAST_SERVER_NAME"
private const val PREFS_SERVER_INDEX = "LAST_SERVER_INDEX" private const val PREFS_SERVER_INDEX = "LAST_SERVER_INDEX"
private const val PROCESS_NAME = "org.amnezia.vpn:amneziaVpnService" private const val PROCESS_NAME = "org.amnezia.vpn:amneziaVpnService"
// private const val STATISTICS_SENDING_TIMEOUT = 1000L private const val NOTIFICATION_ID = 1337
private const val TRAFFIC_STATS_UPDATE_TIMEOUT = 1000L private const val STATISTICS_SENDING_TIMEOUT = 1000L
private const val DISCONNECT_TIMEOUT = 5000L private const val DISCONNECT_TIMEOUT = 5000L
private const val STOP_SERVICE_TIMEOUT = 5000L private const val STOP_SERVICE_TIMEOUT = 5000L
@@ -101,14 +96,8 @@ class AmneziaVpnService : VpnService() {
private var connectionJob: Job? = null private var connectionJob: Job? = null
private var disconnectionJob: Job? = null private var disconnectionJob: Job? = null
private var trafficStatsUpdateJob: Job? = null private var statisticsSendingJob: Job? = null
// private var statisticsSendingJob: Job? = null
private lateinit var networkState: NetworkState private lateinit var networkState: NetworkState
private lateinit var trafficStats: TrafficStats
private var disconnectReceiver: BroadcastReceiver? = null
private var notificationStateReceiver: BroadcastReceiver? = null
private var screenOnReceiver: BroadcastReceiver? = null
private var screenOffReceiver: BroadcastReceiver? = null
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>() private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
private val isActivityConnected private val isActivityConnected
@@ -142,13 +131,13 @@ class AmneziaVpnService : VpnService() {
val messenger = IpcMessenger(msg.replyTo, clientName) val messenger = IpcMessenger(msg.replyTo, clientName)
clientMessengers[msg.replyTo] = messenger clientMessengers[msg.replyTo] = messenger
Log.d(TAG, "Messenger client '$clientName' was registered") Log.d(TAG, "Messenger client '$clientName' was registered")
// if (clientName == ACTIVITY_MESSENGER_NAME && isConnected) launchSendingStatistics() if (clientName == ACTIVITY_MESSENGER_NAME && isConnected) launchSendingStatistics()
} }
Action.UNREGISTER_CLIENT -> { Action.UNREGISTER_CLIENT -> {
clientMessengers.remove(msg.replyTo)?.let { clientMessengers.remove(msg.replyTo)?.let {
Log.d(TAG, "Messenger client '${it.name}' was unregistered") Log.d(TAG, "Messenger client '${it.name}' was unregistered")
// if (it.name == ACTIVITY_MESSENGER_NAME) stopSendingStatistics() if (it.name == ACTIVITY_MESSENGER_NAME) stopSendingStatistics()
} }
} }
@@ -170,10 +159,6 @@ class AmneziaVpnService : VpnService() {
} }
} }
Action.NOTIFICATION_PERMISSION_GRANTED -> {
enableNotification()
}
Action.SET_SAVE_LOGS -> { Action.SET_SAVE_LOGS -> {
Log.saveLogs = msg.data.getBoolean(MSG_SAVE_LOGS) Log.saveLogs = msg.data.getBoolean(MSG_SAVE_LOGS)
} }
@@ -196,7 +181,25 @@ class AmneziaVpnService : VpnService() {
else -> 0 else -> 0
} }
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) } private val notification: Notification by lazy(NONE) {
NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_amnezia_round)
.setShowWhen(false)
.setContentIntent(
PendingIntent.getActivity(
this,
0,
Intent(this, AmneziaActivity::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
.setOngoing(true)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.build()
}
/** /**
* Service overloaded methods * Service overloaded methods
@@ -209,8 +212,6 @@ class AmneziaVpnService : VpnService() {
loadServerData() loadServerData()
launchProtocolStateHandler() launchProtocolStateHandler()
networkState = NetworkState(this, ::reconnect) networkState = NetworkState(this, ::reconnect)
trafficStats = TrafficStats()
registerBroadcastReceivers()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -226,10 +227,7 @@ class AmneziaVpnService : VpnService() {
Log.d(TAG, "Start service") Log.d(TAG, "Start service")
connect(intent?.getStringExtra(MSG_VPN_CONFIG)) connect(intent?.getStringExtra(MSG_VPN_CONFIG))
} }
ServiceCompat.startForeground( ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, foregroundServiceTypeCompat)
this, NOTIFICATION_ID, serviceNotification.buildNotification(serverName, protocolState.value),
foregroundServiceTypeCompat
)
return START_REDELIVER_INTENT return START_REDELIVER_INTENT
} }
@@ -269,7 +267,6 @@ class AmneziaVpnService : VpnService() {
override fun onDestroy() { override fun onDestroy() {
Log.d(TAG, "Destroy service") Log.d(TAG, "Destroy service")
unregisterBroadcastReceivers()
runBlocking { runBlocking {
disconnect() disconnect()
disconnectionJob?.join() disconnectionJob?.join()
@@ -290,63 +287,6 @@ class AmneziaVpnService : VpnService() {
stopSelf() stopSelf()
} }
private fun registerBroadcastReceivers() {
Log.d(TAG, "Register broadcast receivers")
disconnectReceiver = registerBroadcastReceiver(ACTION_DISCONNECT, ContextCompat.RECEIVER_NOT_EXPORTED) {
Log.d(TAG, "Broadcast request received: $ACTION_DISCONNECT")
disconnect()
}
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
arrayOf(
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
Log.d(TAG, "Notification state changed: ${it?.action}, blocked = $state")
if (state == false) {
enableNotification()
} else {
disableNotification()
}
}
} else null
registerScreenStateBroadcastReceivers()
}
private fun registerScreenStateBroadcastReceivers() {
if (serviceNotification.isNotificationEnabled()) {
Log.d(TAG, "Register screen state broadcast receivers")
screenOnReceiver = registerBroadcastReceiver(Intent.ACTION_SCREEN_ON) {
if (isConnected && serviceNotification.isNotificationEnabled()) startTrafficStatsUpdateJob()
}
screenOffReceiver = registerBroadcastReceiver(Intent.ACTION_SCREEN_OFF) {
stopTrafficStatsUpdateJob()
}
}
}
private fun unregisterScreenStateBroadcastReceivers() {
Log.d(TAG, "Unregister screen state broadcast receivers")
unregisterBroadcastReceiver(screenOnReceiver)
unregisterBroadcastReceiver(screenOffReceiver)
screenOnReceiver = null
screenOffReceiver = null
}
private fun unregisterBroadcastReceivers() {
Log.d(TAG, "Unregister broadcast receivers")
unregisterBroadcastReceiver(disconnectReceiver)
unregisterBroadcastReceiver(notificationStateReceiver)
unregisterScreenStateBroadcastReceivers()
disconnectReceiver = null
notificationStateReceiver = null
}
/** /**
* Methods responsible for processing VPN connection * Methods responsible for processing VPN connection
*/ */
@@ -355,8 +295,29 @@ class AmneziaVpnService : VpnService() {
// drop first default UNKNOWN state // drop first default UNKNOWN state
protocolState.drop(1).collect { protocolState -> protocolState.drop(1).collect { protocolState ->
Log.d(TAG, "Protocol state changed: $protocolState") Log.d(TAG, "Protocol state changed: $protocolState")
when (protocolState) {
CONNECTED -> {
networkState.bindNetworkListener()
if (isActivityConnected) launchSendingStatistics()
}
serviceNotification.updateNotification(serverName, protocolState) DISCONNECTED -> {
networkState.unbindNetworkListener()
stopSendingStatistics()
if (!isServiceBound) stopService()
}
DISCONNECTING -> {
networkState.unbindNetworkListener()
stopSendingStatistics()
}
RECONNECTING -> {
stopSendingStatistics()
}
CONNECTING, UNKNOWN -> {}
}
clientMessengers.send { clientMessengers.send {
ServiceEvent.STATUS_CHANGED.packToMessage { ServiceEvent.STATUS_CHANGED.packToMessage {
@@ -365,41 +326,13 @@ class AmneziaVpnService : VpnService() {
} }
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex) } VpnStateStore.store { VpnState(protocolState, serverName, serverIndex) }
when (protocolState) {
CONNECTED -> {
networkState.bindNetworkListener()
// if (isActivityConnected) launchSendingStatistics()
launchTrafficStatsUpdate()
}
DISCONNECTED -> {
networkState.unbindNetworkListener()
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
if (!isServiceBound) stopService()
}
DISCONNECTING -> {
networkState.unbindNetworkListener()
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
}
RECONNECTING -> {
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
}
CONNECTING, UNKNOWN -> {}
}
} }
} }
} }
/* @MainThread @MainThread
private fun launchSendingStatistics() { private fun launchSendingStatistics() {
if (isServiceBound && isConnected) { /* if (isServiceBound && isConnected) {
statisticsSendingJob = mainScope.launch { statisticsSendingJob = mainScope.launch {
while (true) { while (true) {
clientMessenger.send { clientMessenger.send {
@@ -410,62 +343,12 @@ class AmneziaVpnService : VpnService() {
delay(STATISTICS_SENDING_TIMEOUT) delay(STATISTICS_SENDING_TIMEOUT)
} }
} }
} } */
} }
@MainThread @MainThread
private fun stopSendingStatistics() { private fun stopSendingStatistics() {
statisticsSendingJob?.cancel() statisticsSendingJob?.cancel()
} */
@MainThread
private fun enableNotification() {
registerScreenStateBroadcastReceivers()
serviceNotification.updateNotification(serverName, protocolState.value)
launchTrafficStatsUpdate()
}
@MainThread
private fun disableNotification() {
unregisterScreenStateBroadcastReceivers()
stopTrafficStatsUpdateJob()
}
@MainThread
private fun launchTrafficStatsUpdate() {
stopTrafficStatsUpdateJob()
if (isConnected &&
serviceNotification.isNotificationEnabled() &&
getSystemService<PowerManager>()?.isInteractive != false
) {
Log.d(TAG, "Launch traffic stats update")
trafficStats.reset()
startTrafficStatsUpdateJob()
}
}
@MainThread
private fun startTrafficStatsUpdateJob() {
if (trafficStatsUpdateJob == null && trafficStats.isSupported()) {
Log.d(TAG, "Start traffic stats update")
trafficStatsUpdateJob = mainScope.launch {
while (true) {
trafficStats.getSpeed().let { speed ->
if (isConnected) {
serviceNotification.updateSpeed(speed)
}
}
delay(TRAFFIC_STATS_UPDATE_TIMEOUT)
}
}
}
}
@MainThread
private fun stopTrafficStatsUpdateJob() {
Log.d(TAG, "Stop traffic stats update")
trafficStatsUpdateJob?.cancel()
trafficStatsUpdateJob = null
} }
@MainThread @MainThread
@@ -588,7 +471,6 @@ class AmneziaVpnService : VpnService() {
private fun saveServerData(config: JSONObject?) { private fun saveServerData(config: JSONObject?) {
serverName = config?.opt("description") as String? serverName = config?.opt("description") as String?
serverIndex = config?.opt("serverIndex") as Int? ?: -1 serverIndex = config?.opt("serverIndex") as Int? ?: -1
Log.d(TAG, "Save server data: ($serverIndex, $serverName)")
Prefs.save(PREFS_SERVER_NAME, serverName) Prefs.save(PREFS_SERVER_NAME, serverName)
Prefs.save(PREFS_SERVER_INDEX, serverIndex) Prefs.save(PREFS_SERVER_INDEX, serverIndex)
} }
@@ -596,7 +478,6 @@ class AmneziaVpnService : VpnService() {
private fun loadServerData() { private fun loadServerData() {
serverName = Prefs.load<String>(PREFS_SERVER_NAME).ifBlank { null } serverName = Prefs.load<String>(PREFS_SERVER_NAME).ifBlank { null }
if (serverName != null) serverIndex = Prefs.load(PREFS_SERVER_INDEX) if (serverName != null) serverIndex = Prefs.load(PREFS_SERVER_INDEX)
Log.d(TAG, "Load server data: ($serverIndex, $serverName)")
} }
private fun checkPermission(): Boolean = private fun checkPermission(): Boolean =
@@ -613,8 +494,9 @@ class AmneziaVpnService : VpnService() {
companion object { companion object {
fun isRunning(context: Context): Boolean = fun isRunning(context: Context): Boolean =
context.getSystemService<ActivityManager>()!!.runningAppProcesses.any { (context.getSystemService(ACTIVITY_SERVICE) as ActivityManager)
it.processName == PROCESS_NAME && it.importance <= IMPORTANCE_FOREGROUND_SERVICE .runningAppProcesses.any {
} it.processName == PROCESS_NAME && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
}
} }
} }
@@ -32,7 +32,6 @@ enum class Action : IpcMessage {
CONNECT, CONNECT,
DISCONNECT, DISCONNECT,
REQUEST_STATUS, REQUEST_STATUS,
NOTIFICATION_PERMISSION_GRANTED,
SET_SAVE_LOGS SET_SAVE_LOGS
} }
@@ -0,0 +1,189 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.amnezia.vpn;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.Manifest.permission;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.WebView;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
// Gets used by /platforms/android/androidAppListProvider.cpp
public class PackageManagerHelper {
final static String TAG = "PackageManagerHelper";
final static int MIN_CHROME_VERSION = 65;
final static List<String> CHROME_BROWSERS = Arrays.asList(
new String[] {"com.google.android.webview", "com.android.webview", "com.google.chrome"});
private static String getAllAppNames(Context ctx) {
JSONObject output = new JSONObject();
PackageManager pm = ctx.getPackageManager();
List<String> browsers = getBrowserIDs(pm);
List<PackageInfo> packs = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
for (int i = 0; i < packs.size(); i++) {
PackageInfo p = packs.get(i);
// Do not add ourselves and System Apps to the list, unless it might be a browser
if ((!isSystemPackage(p,pm) || browsers.contains(p.packageName))
&& !isSelf(p)) {
String appid = p.packageName;
String appName = p.applicationInfo.loadLabel(pm).toString();
try {
output.put(appid, appName);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
return output.toString();
}
private static Drawable getAppIcon(Context ctx, String id) {
try {
return ctx.getPackageManager().getApplicationIcon(id);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return new ColorDrawable(Color.TRANSPARENT);
}
private static boolean isSystemPackage(PackageInfo pkgInfo, PackageManager pm) {
if( (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0){
// no system app
return false;
}
// For Systems Packages there are Cases where we want to add it anyway:
// Has the use Internet permission (otherwise makes no sense)
// Had at least 1 update (this means it's probably on any AppStore)
// Has a a launch activity (has a ui and is not just a system service)
if(!usesInternet(pkgInfo)){
return true;
}
if(!hadUpdate(pkgInfo)){
return true;
}
if(pm.getLaunchIntentForPackage(pkgInfo.packageName) == null){
// If there is no way to launch this from a homescreen, def a sys package
return true;
}
return false;
}
private static boolean isSelf(PackageInfo pkgInfo) {
return pkgInfo.packageName.equals("org.amnezia.vpn")
|| pkgInfo.packageName.equals("org.amnezia.vpn.debug");
}
private static boolean usesInternet(PackageInfo pkgInfo){
if(pkgInfo.requestedPermissions == null){
return false;
}
for(int i=0; i < pkgInfo.requestedPermissions.length; i++) {
String permission = pkgInfo.requestedPermissions[i];
if(Manifest.permission.INTERNET.equals(permission)){
return true;
}
}
return false;
}
private static boolean hadUpdate(PackageInfo pkgInfo){
return pkgInfo.lastUpdateTime > pkgInfo.firstInstallTime;
}
// Returns List of all Packages that can classify themselves as browsers
private static List<String> getBrowserIDs(PackageManager pm) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.amnezia.org/"));
intent.addCategory(Intent.CATEGORY_BROWSABLE);
// We've tried using PackageManager.MATCH_DEFAULT_ONLY flag and found that browsers that
// are not set as the default browser won't be matched even if they had CATEGORY_DEFAULT set
// in the intent filter
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
List<String> browsers = new ArrayList<String>();
for (int i = 0; i < resolveInfos.size(); i++) {
ResolveInfo info = resolveInfos.get(i);
String browserID = info.activityInfo.packageName;
browsers.add(browserID);
}
return browsers;
}
// Gets called in AndroidAuthenticationListener;
public static boolean isWebViewSupported(Context ctx) {
Log.v(TAG, "Checking if installed Webview is compatible with FxA");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// The default Webview is able do to FXA
return true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PackageInfo pi = WebView.getCurrentWebViewPackage();
if (CHROME_BROWSERS.contains(pi.packageName)) {
return isSupportedChromeBrowser(pi);
}
return isNotAncientBrowser(pi);
}
// Before O the webview is hardcoded, but we dont know which package it is.
// Check if com.google.android.webview is installed
PackageManager pm = ctx.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo("com.google.android.webview", 0);
return isSupportedChromeBrowser(pi);
} catch (PackageManager.NameNotFoundException e) {
}
// Otherwise check com.android.webview
try {
PackageInfo pi = pm.getPackageInfo("com.android.webview", 0);
return isSupportedChromeBrowser(pi);
} catch (PackageManager.NameNotFoundException e) {
}
Log.e(TAG, "Android System WebView is not found");
// Giving up :(
return false;
}
private static boolean isSupportedChromeBrowser(PackageInfo pi) {
Log.d(TAG, "Checking Chrome Based Browser: " + pi.packageName);
Log.d(TAG, "version name: " + pi.versionName);
Log.d(TAG, "version code: " + pi.versionCode);
try {
String versionCode = pi.versionName.split(Pattern.quote(" "))[0];
String majorVersion = versionCode.split(Pattern.quote("."))[0];
int version = Integer.parseInt(majorVersion);
return version >= MIN_CHROME_VERSION;
} catch (Exception e) {
Log.e(TAG, "Failed to check Chrome Version Code " + pi.versionName);
return false;
}
}
private static boolean isNotAncientBrowser(PackageInfo pi) {
// Not a google chrome - So the version name is worthless
// Lets just make sure the WebView
// used is not ancient ==> Was updated in at least the last 365 days
Log.d(TAG, "Checking Chrome Based Browser: " + pi.packageName);
Log.d(TAG, "version name: " + pi.versionName);
Log.d(TAG, "version code: " + pi.versionCode);
double oneYearInMillis = 31536000000L;
return pi.lastUpdateTime > (System.currentTimeMillis() - oneYearInMillis);
}
}
@@ -1,180 +0,0 @@
package org.amnezia.vpn
import android.Manifest.permission
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.NotificationChannelCompat.Builder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.Action
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.TrafficStats.TrafficData
private const val TAG = "ServiceNotification"
private const val OLD_NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
private const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notifications"
const val NOTIFICATION_ID = 1337
private const val GET_ACTIVITY_REQUEST_CODE = 0
private const val CONNECT_REQUEST_CODE = 1
private const val DISCONNECT_REQUEST_CODE = 2
class ServiceNotification(private val context: Context) {
private val upDownSymbols = when (Build.BRAND) {
"Infinix" -> '˅' to '˄'
else -> '↓' to '↑'
}
private val notificationManager = NotificationManagerCompat.from(context)
private val notificationBuilder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setOngoing(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setContentIntent(
PendingIntent.getActivity(
context,
GET_ACTIVITY_REQUEST_CODE,
Intent(context, AmneziaActivity::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
private val zeroSpeed: String = with(TrafficData.ZERO) {
formatSpeedString(rxString, txString)
}
fun buildNotification(serverName: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null
Log.d(TAG, "Build notification: $serverName, $state")
return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round)
.setContentTitle(serverName ?: "AmneziaVPN")
.setContentText(context.getString(state))
.setSubText(speedString)
.setWhen(System.currentTimeMillis())
.clearActions()
.apply {
getAction(state)?.let {
addAction(it)
}
}
.build()
}
private fun buildNotification(speed: TrafficData): Notification =
notificationBuilder
.setWhen(System.currentTimeMillis())
.setSubText(getSpeedString(speed))
.build()
fun isNotificationEnabled(): Boolean {
if (!context.isNotificationPermissionGranted()) return false
if (!notificationManager.areNotificationsEnabled()) return false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true
}
return true
}
@SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, state: ProtocolState) {
if (context.isNotificationPermissionGranted()) {
Log.d(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, state))
}
}
@SuppressLint("MissingPermission")
fun updateSpeed(speed: TrafficData) {
if (context.isNotificationPermissionGranted()) {
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
}
}
private fun getSpeedString(traffic: TrafficData) =
if (traffic == TrafficData.ZERO) zeroSpeed
else formatSpeedString(traffic.rxString, traffic.txString)
private fun formatSpeedString(rx: String, tx: String) = with(upDownSymbols) { "$first $rx $second $tx" }
private fun getAction(state: ProtocolState): Action? {
return when (state) {
CONNECTED -> {
Action(
0, context.getString(R.string.disconnect),
PendingIntent.getBroadcast(
context,
DISCONNECT_REQUEST_CODE,
Intent(ACTION_DISCONNECT).apply {
setPackage("org.amnezia.vpn")
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
DISCONNECTED -> {
Action(
0, context.getString(R.string.connect),
createServicePendingIntent(
context,
CONNECT_REQUEST_CODE,
Intent(context, AmneziaVpnService::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
else -> null
}
}
private val createServicePendingIntent: (Context, Int, Intent, Int) -> PendingIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PendingIntent::getForegroundService
} else {
PendingIntent::getService
}
companion object {
fun createNotificationChannel(context: Context) {
with(NotificationManagerCompat.from(context)) {
deleteNotificationChannel(OLD_NOTIFICATION_CHANNEL_ID)
createNotificationChannel(
Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setShowBadge(false)
.setSound(null, null)
.setVibrationEnabled(false)
.setLightsEnabled(false)
.setName("AmneziaVPN")
.setDescription(context.resources.getString(R.string.notificationChannelDescription))
.build()
)
}
}
}
}
fun Context.isNotificationPermissionGranted(): Boolean =
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
ContextCompat.checkSelfPermission(this, permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
@@ -3,7 +3,9 @@ package org.amnezia.vpn
import android.app.AlertDialog import android.app.AlertDialog
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent 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_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.net.VpnService import android.net.VpnService
@@ -31,9 +33,11 @@ class VpnRequestActivity : ComponentActivity() {
val requestIntent = VpnService.prepare(applicationContext) val requestIntent = VpnService.prepare(applicationContext)
if (requestIntent != null) { if (requestIntent != null) {
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) { if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
userPresentReceiver = registerBroadcastReceiver(Intent.ACTION_USER_PRESENT) { userPresentReceiver = object : BroadcastReceiver() {
requestLauncher.launch(requestIntent) override fun onReceive(context: Context?, intent: Intent?) =
requestLauncher.launch(requestIntent)
} }
registerReceiver(userPresentReceiver, IntentFilter(Intent.ACTION_USER_PRESENT))
} else { } else {
requestLauncher.launch(requestIntent) requestLauncher.launch(requestIntent)
} }
@@ -45,8 +49,9 @@ class VpnRequestActivity : ComponentActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
unregisterBroadcastReceiver(userPresentReceiver) userPresentReceiver?.let {
userPresentReceiver = null unregisterReceiver(it)
}
super.onDestroy() super.onDestroy()
} }
@@ -4,8 +4,6 @@ import android.app.Application
import androidx.datastore.core.MultiProcessDataStoreFactory import androidx.datastore.core.MultiProcessDataStoreFactory
import androidx.datastore.core.Serializer import androidx.datastore.core.Serializer
import androidx.datastore.dataStoreFile import androidx.datastore.dataStoreFile
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.ObjectInputStream import java.io.ObjectInputStream
import java.io.ObjectOutputStream import java.io.ObjectOutputStream
@@ -61,8 +59,7 @@ private class VpnStateSerializer : Serializer<VpnState> {
override suspend fun readFrom(input: InputStream): VpnState { override suspend fun readFrom(input: InputStream): VpnState {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val bios = ByteArrayInputStream(input.readBytes()) ObjectInputStream(input).use {
ObjectInputStream(bios).use {
it.readObject() as VpnState it.readObject() as VpnState
} }
} }
@@ -70,11 +67,9 @@ private class VpnStateSerializer : Serializer<VpnState> {
override suspend fun writeTo(t: VpnState, output: OutputStream) { override suspend fun writeTo(t: VpnState, output: OutputStream) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val baos = ByteArrayOutputStream() ObjectOutputStream(output).use {
ObjectOutputStream(baos).use {
it.writeObject(t) it.writeObject(t)
} }
output.write(baos.toByteArray())
} }
} }
} }
@@ -17,7 +17,6 @@ object QtAndroidController {
external fun onServiceError() external fun onServiceError()
external fun onVpnPermissionRejected() external fun onVpnPermissionRejected()
external fun onNotificationStateChanged()
external fun onVpnStateChanged(stateCode: Int) external fun onVpnStateChanged(stateCode: Int)
external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long) external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long)
-2
View File
@@ -17,7 +17,5 @@ android {
} }
dependencies { dependencies {
implementation(libs.androidx.core)
implementation(libs.kotlinx.coroutines)
implementation(libs.androidx.security.crypto) implementation(libs.androidx.security.crypto)
} }
+3 -5
View File
@@ -109,11 +109,9 @@ object Log {
"${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}" "${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}"
fun clearLogs() { fun clearLogs() {
if (logDir.exists()) { withLock {
withLock { logFile.delete()
logFile.delete() rotateLogFile.delete()
rotateLogFile.delete()
}
} }
} }
@@ -1,21 +1,16 @@
package org.amnezia.vpn.util.net package org.amnezia.vpn.util.net
import java.net.Inet4Address
import java.net.InetAddress import java.net.InetAddress
data class InetEndpoint(val address: InetAddress, val port: Int) { data class InetEndpoint(val address: InetAddress, val port: Int) {
override fun toString(): String = if (address is Inet4Address) { override fun toString(): String = "${address.hostAddress}:$port"
"${address.ip}:$port"
} else {
"[${address.ip}]:$port"
}
companion object { companion object {
fun parse(data: String): InetEndpoint { fun parse(data: String): InetEndpoint {
val i = data.lastIndexOf(':') val split = data.split(":")
val address = parseInetAddress(data.substring(0, i)) val address = parseInetAddress(split.first())
val port = data.substring(i + 1).toInt() val port = split.last().toInt()
return InetEndpoint(address, port) return InetEndpoint(address, port)
} }
} }
@@ -9,11 +9,7 @@ data class InetNetwork(val address: InetAddress, val mask: Int) {
constructor(address: InetAddress) : this(address, address.maxPrefixLength) constructor(address: InetAddress) : this(address, address.maxPrefixLength)
val isIpv4: Boolean = address is Inet4Address override fun toString(): String = "${address.hostAddress}/$mask"
val isIpv6: Boolean
get() = !isIpv4
override fun toString(): String = "${address.ip}/$mask"
companion object { companion object {
fun parse(data: String): InetNetwork { fun parse(data: String): InetNetwork {
@@ -3,17 +3,12 @@ package org.amnezia.vpn.util.net
import java.net.InetAddress import java.net.InetAddress
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
internal class IpAddress private constructor(private val address: UByteArray) : Comparable<IpAddress> { class IpAddress private constructor(private val address: UByteArray) : Comparable<IpAddress> {
val size: Int = address.size val size: Int = address.size
val lastIndex: Int = address.lastIndex val lastIndex: Int = address.lastIndex
val maxMask: Int = size * 8 val maxMask: Int = size * 8
@OptIn(ExperimentalStdlibApi::class)
val hexFormat: HexFormat by lazy {
HexFormat { number.removeLeadingZeros = true }
}
constructor(inetAddress: InetAddress) : this(inetAddress.address.asUByteArray()) constructor(inetAddress: InetAddress) : this(inetAddress.address.asUByteArray())
constructor(ipAddress: String) : this(parseInetAddress(ipAddress)) constructor(ipAddress: String) : this(parseInetAddress(ipAddress))
@@ -48,8 +43,6 @@ internal class IpAddress private constructor(private val address: UByteArray) :
return copy return copy
} }
fun isMinIp(): Boolean = address.all { it == 0x00u.toUByte() }
fun isMaxIp(): Boolean = address.all { it == 0xffu.toUByte() } fun isMaxIp(): Boolean = address.all { it == 0xffu.toUByte() }
override fun compareTo(other: IpAddress): Int { override fun compareTo(other: IpAddress): Int {
@@ -81,14 +74,12 @@ internal class IpAddress private constructor(private val address: UByteArray) :
private fun toIpv6String(): String { private fun toIpv6String(): String {
val sb = StringBuilder() val sb = StringBuilder()
var i = 0 var i = 0
var block: Int
while (i < size) { while (i < size) {
block = address[i++].toInt() shl 8 sb.append(address[i++].toHexString())
block += address[i++].toInt() sb.append(address[i++].toHexString())
sb.append(block.toHexString(hexFormat))
sb.append(':') sb.append(':')
} }
sb.deleteAt(sb.lastIndex) sb.deleteAt(sb.lastIndex)
return convertIpv6ToCanonicalForm(sb.toString()) return sb.toString()
} }
} }
@@ -2,24 +2,14 @@ package org.amnezia.vpn.util.net
import java.net.InetAddress import java.net.InetAddress
class IpRange internal constructor( class IpRange(private val start: IpAddress, private val end: IpAddress) : Comparable<IpRange> {
internal val start: IpAddress,
internal val end: IpAddress
) : Comparable<IpRange> {
init { init {
if (start.size != end.size) { if (start > end) throw IllegalArgumentException("Start IP: $start is greater then end IP: $end")
throw IllegalArgumentException(
"Unable to create a range between IPv4 and IPv6 addresses (start IP: [$start], end IP: [$end])"
)
}
if (start > end) throw IllegalArgumentException("Start IP: [$start] is greater then end IP: [$end]")
} }
private constructor(addresses: Pair<IpAddress, IpAddress>) : this(addresses.first, addresses.second) private constructor(addresses: Pair<IpAddress, IpAddress>) : this(addresses.first, addresses.second)
internal constructor(ipAddress: IpAddress) : this(ipAddress, ipAddress)
constructor(inetAddress: InetAddress, mask: Int) : this(from(inetAddress, mask)) constructor(inetAddress: InetAddress, mask: Int) : this(from(inetAddress, mask))
constructor(address: String, mask: Int) : this(parseInetAddress(address), mask) constructor(address: String, mask: Int) : this(parseInetAddress(address), mask)
@@ -32,13 +22,6 @@ class IpRange internal constructor(
private fun isIntersect(other: IpRange): Boolean = private fun isIntersect(other: IpRange): Boolean =
(start <= other.end) && (end >= other.start) (start <= other.end) && (end >= other.start)
operator fun plus(other: IpRange): IpRange? {
if (start > other.end && !start.isMinIp() && start.dec() == other.end) return IpRange(other.start, end)
if (end < other.start && !end.isMaxIp() && end.inc() == other.start) return IpRange(start, other.end)
if (!isIntersect(other)) return null
return IpRange(minOf(start, other.start), maxOf(end, other.end))
}
operator fun minus(other: IpRange): List<IpRange>? { operator fun minus(other: IpRange): List<IpRange>? {
if (this in other) return emptyList() if (this in other) return emptyList()
if (!isIntersect(other)) return null if (!isIntersect(other)) return null
@@ -111,7 +94,9 @@ class IpRange internal constructor(
return result return result
} }
override fun toString(): String = if (start == end) "<$start>" else "<$start - $end>" override fun toString(): String {
return "$start - $end"
}
companion object { companion object {
private fun from(inetAddress: InetAddress, mask: Int): Pair<IpAddress, IpAddress> { private fun from(inetAddress: InetAddress, mask: Int): Pair<IpAddress, IpAddress> {
@@ -1,35 +1,15 @@
package org.amnezia.vpn.util.net package org.amnezia.vpn.util.net
class IpRangeSet { class IpRangeSet(ipRange: IpRange = IpRange("0.0.0.0", 0)) {
private val ranges = sortedSetOf<IpRange>() private val ranges = sortedSetOf(ipRange)
fun add(ipRange: IpRange) {
val iterator = ranges.iterator()
var rangeToAdd = ipRange
run {
while (iterator.hasNext()) {
val curRange = iterator.next()
if (rangeToAdd.end < curRange.start &&
!rangeToAdd.end.isMaxIp() &&
rangeToAdd.end.inc() != curRange.start) break
(curRange + rangeToAdd)?.let { resultRange ->
if (resultRange == curRange) return@run
iterator.remove()
rangeToAdd = resultRange
}
}
ranges += rangeToAdd
}
}
fun remove(ipRange: IpRange) { fun remove(ipRange: IpRange) {
val iterator = ranges.iterator() val iterator = ranges.iterator()
val splitRanges = mutableListOf<IpRange>() val splitRanges = mutableListOf<IpRange>()
while (iterator.hasNext()) { while (iterator.hasNext()) {
val curRange = iterator.next() val range = iterator.next()
if (ipRange.end < curRange.start) break (range - ipRange)?.let { resultRanges ->
(curRange - ipRange)?.let { resultRanges ->
iterator.remove() iterator.remove()
splitRanges += resultRanges splitRanges += resultRanges
} }
@@ -37,7 +17,10 @@ class IpRangeSet {
ranges += splitRanges ranges += splitRanges
} }
fun subnets(): List<InetNetwork> = ranges.map(IpRange::subnets).flatten() fun subnets(): List<InetNetwork> =
ranges.map(IpRange::subnets).flatten()
override fun toString(): String = ranges.toString() override fun toString(): String {
return ranges.toString()
}
} }
@@ -10,9 +10,7 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkRequest import android.net.NetworkRequest
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import androidx.core.content.getSystemService
import kotlin.LazyThreadSafetyMode.NONE import kotlin.LazyThreadSafetyMode.NONE
import kotlinx.coroutines.delay
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
private const val TAG = "NetworkState" private const val TAG = "NetworkState"
@@ -30,7 +28,7 @@ class NetworkState(
} }
private val connectivityManager: ConnectivityManager by lazy(NONE) { private val connectivityManager: ConnectivityManager by lazy(NONE) {
context.getSystemService<ConnectivityManager>()!! context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
} }
private val networkRequest: NetworkRequest by lazy(NONE) { private val networkRequest: NetworkRequest by lazy(NONE) {
@@ -82,24 +80,13 @@ class NetworkState(
} }
} }
suspend fun bindNetworkListener() { fun bindNetworkListener() {
if (isListenerBound) return if (isListenerBound) return
Log.d(TAG, "Bind network listener") Log.d(TAG, "Bind network listener")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler) connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try { connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to bind network listener: $e")
// Android 11 bug: https://issuetracker.google.com/issues/175055271
if (e.message?.startsWith("Package android does not belong to") == true) {
delay(1000)
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
} else {
throw e
}
}
} else { } else {
connectivityManager.requestNetwork(networkRequest, networkCallback) connectivityManager.requestNetwork(networkRequest, networkCallback)
} }
@@ -5,14 +5,12 @@ import android.net.ConnectivityManager
import android.net.InetAddresses import android.net.InetAddresses
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Build import android.os.Build
import androidx.core.content.getSystemService
import java.lang.reflect.InvocationTargetException
import java.net.Inet4Address import java.net.Inet4Address
import java.net.Inet6Address import java.net.Inet6Address
import java.net.InetAddress import java.net.InetAddress
fun getLocalNetworks(context: Context, ipv6: Boolean): List<InetNetwork> { fun getLocalNetworks(context: Context, ipv6: Boolean): List<InetNetwork> {
val connectivityManager = context.getSystemService<ConnectivityManager>()!! val connectivityManager = context.getSystemService(ConnectivityManager::class.java)
connectivityManager.activeNetwork?.let { network -> connectivityManager.activeNetwork?.let { network ->
val netCapabilities = connectivityManager.getNetworkCapabilities(network) val netCapabilities = connectivityManager.getNetworkCapabilities(network)
val linkProperties = connectivityManager.getLinkProperties(network) val linkProperties = connectivityManager.getLinkProperties(network)
@@ -41,28 +39,8 @@ private val parseNumericAddressCompat: (String) -> InetAddress =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
InetAddresses::parseNumericAddress InetAddresses::parseNumericAddress
} else { } else {
try { val m = InetAddress::class.java.getMethod("parseNumericAddress", String::class.java)
val m = InetAddress::class.java.getMethod("parseNumericAddress", String::class.java) fun(address: String): InetAddress {
fun(address: String): InetAddress { return m.invoke(null, address) as InetAddress
try {
return m.invoke(null, address) as InetAddress
} catch (e: InvocationTargetException) {
throw e.cause ?: e
}
}
} catch (_: NoSuchMethodException) {
fun(address: String): InetAddress {
return InetAddress.getByName(address)
}
} }
} }
internal fun convertIpv6ToCanonicalForm(ipv6: String): String = ipv6
.replace("((?:(?:^|:)0+\\b){2,}):?(?!\\S*\\b\\1:0+\\b)(\\S*)".toRegex(), "::$2")
internal val InetAddress.ip: String
get() = if (this is Inet4Address) {
hostAddress!!
} else {
convertIpv6ToCanonicalForm(hostAddress!!)
}
@@ -1,93 +0,0 @@
package org.amnezia.vpn.util.net
import android.net.TrafficStats
import android.os.Build
import android.os.Process
import android.os.SystemClock
import kotlin.math.roundToLong
private const val BYTE = 1L
private const val KiB = BYTE shl 10
private const val MiB = KiB shl 10
private const val GiB = MiB shl 10
private const val TiB = GiB shl 10
class TrafficStats {
private var lastTrafficData = TrafficData.ZERO
private var lastTimestamp = 0L
private val getTrafficDataCompat: () -> TrafficData =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val iface = "tun0"
fun(): TrafficData {
return TrafficData(TrafficStats.getRxBytes(iface), TrafficStats.getTxBytes(iface))
}
} else {
val uid = Process.myUid()
fun(): TrafficData {
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
}
}
fun reset() {
lastTrafficData = getTrafficDataCompat()
lastTimestamp = SystemClock.elapsedRealtime()
}
fun isSupported(): Boolean =
lastTrafficData.rx != TrafficStats.UNSUPPORTED.toLong() && lastTrafficData.tx != TrafficStats.UNSUPPORTED.toLong()
fun getSpeed(): TrafficData {
val timestamp = SystemClock.elapsedRealtime()
val elapsedSeconds = (timestamp - lastTimestamp) / 1000.0
val trafficData = getTrafficDataCompat()
val speed = trafficData.diff(lastTrafficData, elapsedSeconds)
lastTrafficData = trafficData
lastTimestamp = timestamp
return speed
}
class TrafficData(val rx: Long, val tx: Long) {
private var _rxString: String? = null
val rxString: String
get() {
if (_rxString == null) _rxString = rx.speedToString()
return _rxString ?: throw AssertionError("Set to null by another thread")
}
private var _txString: String? = null
val txString: String
get() {
if (_txString == null) _txString = tx.speedToString()
return _txString ?: throw AssertionError("Set to null by another thread")
}
fun diff(other: TrafficData, elapsedSeconds: Double): TrafficData {
val rx = ((this.rx - other.rx) / elapsedSeconds).round()
val tx = ((this.tx - other.tx) / elapsedSeconds).round()
return if (rx == 0L && tx == 0L) ZERO else TrafficData(rx, tx)
}
private fun Double.round() = if (isNaN()) 0L else roundToLong()
private fun Long.speedToString() =
when {
this < KiB -> formatSize(this, BYTE, "B/s")
this < MiB -> formatSize(this, KiB, "KiB/s")
this < GiB -> formatSize(this, MiB, "MiB/s")
this < TiB -> formatSize(this, GiB, "GiB/s")
else -> formatSize(this, TiB, "TiB/s")
}
private fun formatSize(bytes: Long, divider: Long, unit: String): String {
val s = (bytes.toDouble() / divider * 100).roundToLong() / 100.0
return "${s.toString().removeSuffix(".0")} $unit"
}
companion object {
val ZERO: TrafficData = TrafficData(0L, 0L)
}
}
}
@@ -37,8 +37,8 @@ open class WireguardConfig protected constructor(
open fun appendPeerLine(sb: StringBuilder) = with(sb) { open fun appendPeerLine(sb: StringBuilder) = with(sb) {
appendLine("public_key=$publicKeyHex") appendLine("public_key=$publicKeyHex")
routes.filter { it.include }.forEach { route -> routes.forEach { route ->
appendLine("allowed_ip=${route.inetNetwork}") appendLine("allowed_ip=$route")
} }
appendLine("endpoint=$endpoint") appendLine("endpoint=$endpoint")
if (persistentKeepalive != 0) if (persistentKeepalive != 0)
+4 -2
View File
@@ -26,6 +26,7 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
set(HEADERS ${HEADERS} set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
@@ -34,6 +35,7 @@ set(HEADERS ${HEADERS}
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
@@ -47,8 +49,8 @@ foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.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}/libovpnutil.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/librsapss.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 ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl3/android/${abi}/libcrypto_3.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl3/android/${abi}/libssl_3.so
) )
endforeach() endforeach()
+1 -1
View File
@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/MobileUtils.mm
) )
@@ -107,7 +108,6 @@ target_sources(${PROJECT} PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift ${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift ${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift ${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
) )
target_sources(${PROJECT} PRIVATE target_sources(${PROJECT} PRIVATE
+8 -6
View File
@@ -3,13 +3,15 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) #include "core/controllers/serverController.h"
: WireguardConfigurator(settings, serverController, true, parent)
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
: WireguardConfigurator(settings, true, parent)
{ {
} }
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode); QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
@@ -39,8 +41,8 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
jsonConfig[config_key::mtu] = jsonConfig[config_key::mtu] = containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu); value(config_key::mtu).toString(protocols::awg::defaultMtu);
return QJsonDocument(jsonConfig).toJson(); return QJsonDocument(jsonConfig).toJson();
} }
+2 -2
View File
@@ -9,10 +9,10 @@ class AwgConfigurator : public WireguardConfigurator
{ {
Q_OBJECT Q_OBJECT
public: public:
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
}; };
#endif // AWGCONFIGURATOR_H #endif // AWGCONFIGURATOR_H
+15 -12
View File
@@ -1,30 +1,33 @@
#include "cloak_configurator.h" #include "cloak_configurator.h"
#include <QFile> #include <QFile>
#include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument>
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h" #include "core/controllers/serverController.h"
#include "containers/containers_defs.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
: ConfiguratorBase(settings, serverController, parent) ConfiguratorBase(settings, parent)
{ {
} }
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
QString cloakPublicKey = ServerController serverController(m_settings);
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
QString cloakPublicKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
cloakPublicKey.replace("\n", ""); cloakPublicKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return ""; return "";
} }
QString cloakBypassUid = QString cloakBypassUid = serverController.getTextFileFromContainer(container, credentials,
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckBypassUidKeyPath, errorCode); amnezia::protocols::cloak::ckBypassUidKeyPath, errorCode);
cloakBypassUid.replace("\n", ""); cloakBypassUid.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -44,8 +47,8 @@ QString CloakConfigurator::createConfig(const ServerCredentials &credentials, Do
config.insert("RemoteHost", credentials.hostName); config.insert("RemoteHost", credentials.hostName);
config.insert("RemotePort", "$CLOAK_SERVER_PORT"); config.insert("RemotePort", "$CLOAK_SERVER_PORT");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(), QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig)); serverController.genVarsForScript(credentials, container, containerConfig));
return textCfg; return textCfg;
} }
+2 -2
View File
@@ -11,10 +11,10 @@ class CloakConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
}; };
#endif // CLOAK_CONFIGURATOR_H #endif // CLOAK_CONFIGURATOR_H
+2 -2
View File
@@ -1,7 +1,7 @@
#include "configurator_base.h" #include "configurator_base.h"
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent)
: QObject { parent }, m_settings(settings), m_serverController(serverController) : QObject { parent }, m_settings(settings)
{ {
} }
+2 -5
View File
@@ -5,17 +5,16 @@
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "core/defs.h" #include "core/defs.h"
#include "core/controllers/serverController.h"
#include "settings.h" #include "settings.h"
class ConfiguratorBase : public QObject class ConfiguratorBase : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); explicit ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container, virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0; const QJsonObject &containerConfig, ErrorCode errorCode) = 0;
virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString); QString &protocolConfigString);
@@ -26,8 +25,6 @@ protected:
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString); void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;
}; };
#endif // CONFIGURATORBASE_H #endif // CONFIGURATORBASE_H
+17 -13
View File
@@ -9,18 +9,18 @@
#include <QUuid> #include <QUuid>
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h" #include "core/scripts_registry.h"
#include "core/server_defs.h" #include "core/server_defs.h"
#include "core/controllers/serverController.h"
#include "utilities.h" #include "utilities.h"
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent)
: ConfiguratorBase(settings, serverController, parent) : ConfiguratorBase(settings, parent)
{ {
} }
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials,
ErrorCode &errorCode) DockerContainer container, ErrorCode errorCode)
{ {
Ikev2Configurator::ConnectionData connData; Ikev2Configurator::ConnectionData connData;
connData.host = credentials.hostName; connData.host = credentials.hostName;
@@ -39,14 +39,18 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"") "--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
.arg(connData.clientId); .arg(connData.clientId);
errorCode = m_serverController->runContainerScript(credentials, container, scriptCreateCert); ServerController serverController(m_settings);
errorCode = serverController.runContainerScript(credentials, container, scriptCreateCert);
QString scriptExportCert = QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"")
QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"").arg(connData.password).arg(connData.clientId).arg(certFileName); .arg(connData.password)
errorCode = m_serverController->runContainerScript(credentials, container, scriptExportCert); .arg(connData.clientId)
.arg(certFileName);
errorCode = serverController.runContainerScript(credentials, container, scriptExportCert);
connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, certFileName, errorCode); connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, errorCode);
connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode); connData.caCert =
serverController.getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode);
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size(); qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size(); qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();
@@ -54,8 +58,8 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
return connData; return connData;
} }
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
Q_UNUSED(containerConfig) Q_UNUSED(containerConfig)
+3 -3
View File
@@ -11,7 +11,7 @@ class Ikev2Configurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
struct ConnectionData { struct ConnectionData {
QByteArray clientCert; // p12 client cert QByteArray clientCert; // p12 client cert
@@ -22,14 +22,14 @@ public:
}; };
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
QString genIkev2Config(const ConnectionData &connData); QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData); QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData); QString genStrongSwanConfig(const ConnectionData &connData);
ConnectionData prepareIkev2Config(const ServerCredentials &credentials, ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode); DockerContainer container, ErrorCode errorCode);
}; };
#endif // IKEV2_CONFIGURATOR_H #endif // IKEV2_CONFIGURATOR_H
+30 -21
View File
@@ -24,14 +24,14 @@
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <openssl/x509.h> #include <openssl/x509.h>
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
QObject *parent) : ConfiguratorBase(settings, parent)
: ConfiguratorBase(settings, serverController, parent)
{ {
} }
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials, OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode) DockerContainer container,
ErrorCode errorCode)
{ {
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest(); OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
connData.host = credentials.hostName; connData.host = credentials.hostName;
@@ -43,7 +43,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath).arg(connData.clientId); QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath).arg(connData.clientId);
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName); ServerController serverController(m_settings);
errorCode = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
@@ -53,16 +54,18 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData; return connData;
} }
connData.caCert = connData.caCert = serverController.getTextFileFromContainer(container, credentials,
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode); amnezia::protocols::openvpn::caCertPath, errorCode);
connData.clientCert = m_serverController->getTextFileFromContainer( connData.clientCert = serverController.getTextFileFromContainer(
container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode); container, credentials,
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode); connData.taKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::openvpn::taKeyPath, errorCode);
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
errorCode = ErrorCode::SshScpFailureError; errorCode = ErrorCode::SshScpFailureError;
@@ -72,10 +75,12 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
} }
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), ServerController serverController(m_settings);
m_serverController->genVarsForScript(credentials, container, containerConfig)); QString config =
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode); ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -116,16 +121,18 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
if (!isApiConfig) { if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*"); QRegularExpression regex("redirect-gateway.*");
config.replace(regex, ""); config.replace(regex, "");
if (!m_settings->isSitesSplitTunnelingEnabled()) { if (m_settings->routeMode() == Settings::VpnAllSites) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak // Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n"); config.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { }
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway // no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { }
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif #endif
@@ -189,10 +196,12 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerC
.arg(ContainerProps::containerToString(container)) .arg(ContainerProps::containerToString(container))
.arg(clientId); .arg(clientId);
ServerController serverController(m_settings);
QStringList scriptList { script_import, script_sign }; QStringList scriptList { script_import, script_sign };
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container)); QString script = serverController.replaceVars(scriptList.join("\n"),
serverController.genVarsForScript(credentials, container));
return m_serverController->runScript(credentials, script); return serverController.runScript(credentials, script);
} }
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
@@ -226,8 +235,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, "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, "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()), X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC,
clientIdUtf8.size(), -1, 0); reinterpret_cast<unsigned char const *>(clientIdUtf8.data()), clientIdUtf8.size(), -1, 0);
// 4. set public key of x509 req // 4. set public key of x509 req
ret = X509_REQ_set_pubkey(x509_req, pKey); ret = X509_REQ_set_pubkey(x509_req, pKey);
+3 -3
View File
@@ -11,7 +11,7 @@ class OpenVpnConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
struct ConnectionData struct ConnectionData
{ {
@@ -25,7 +25,7 @@ public:
}; };
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString); QString &protocolConfigString);
@@ -36,7 +36,7 @@ public:
private: private:
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode); ErrorCode errorCode);
ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId); ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId);
}; };
@@ -1,23 +1,25 @@
#include "shadowsocks_configurator.h" #include "shadowsocks_configurator.h"
#include <QFile> #include <QFile>
#include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument>
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "core/controllers/serverController.h" #include "core/controllers/serverController.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
QObject *parent) ConfiguratorBase(settings, parent)
: ConfiguratorBase(settings, serverController, parent)
{ {
} }
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
QString ssKey = ServerController serverController(m_settings);
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
QString ssKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
ssKey.replace("\n", ""); ssKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -32,9 +34,10 @@ QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentia
config.insert("timeout", 60); config.insert("timeout", 60);
config.insert("method", "$SHADOWSOCKS_CIPHER"); config.insert("method", "$SHADOWSOCKS_CIPHER");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
// qDebug().noquote() << textCfg; QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
serverController.genVarsForScript(credentials, container, containerConfig));
//qDebug().noquote() << textCfg;
return textCfg; return textCfg;
} }
@@ -10,10 +10,10 @@ class ShadowSocksConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
}; };
#endif // SHADOWSOCKS_CONFIGURATOR_H #endif // SHADOWSOCKS_CONFIGURATOR_H
+4 -3
View File
@@ -17,8 +17,8 @@
#include "core/server_defs.h" #include "core/server_defs.h"
#include "utilities.h" #include "utilities.h"
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
: ConfiguratorBase(settings, serverController, parent) : ConfiguratorBase(settings, parent)
{ {
} }
@@ -82,7 +82,8 @@ void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
// p->setNativeArguments(QString("%1@%2") // p->setNativeArguments(QString("%1@%2")
// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData)); // .arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
} else { } 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 #else
p->setProgram("/bin/bash"); p->setProgram("/bin/bash");
+1 -1
View File
@@ -11,7 +11,7 @@ class SshConfigurator : ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QProcessEnvironment prepareEnv(); QProcessEnvironment prepareEnv();
QString convertOpenSShKey(const QString &key); QString convertOpenSShKey(const QString &key);
+32 -23
View File
@@ -19,13 +19,15 @@
#include "settings.h" #include "settings.h"
#include "utilities.h" #include "utilities.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent)
bool isAwg, QObject *parent) : ConfiguratorBase(settings, parent), m_isAwg(isAwg)
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
{ {
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath; m_serverConfigPath =
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath; m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath; 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_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard; m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
@@ -65,7 +67,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials, WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig,
ErrorCode errorCode)
{ {
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys(); WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName; connData.host = credentials.hostName;
@@ -76,6 +79,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData; return connData;
} }
ServerController serverController(m_settings);
// Get list of already created clients (only IP addresses) // Get list of already created clients (only IP addresses)
QString nextIpNumber; QString nextIpNumber;
{ {
@@ -86,7 +91,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return ErrorCode::NoError; return ErrorCode::NoError;
}; };
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut); errorCode = serverController.runContainerScript(credentials, container, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
@@ -108,7 +113,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
} }
} }
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); QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
if (l.isEmpty()) { if (l.isEmpty()) {
@@ -122,13 +128,14 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
} }
// Get keys // Get keys
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode); connData.serverPubKey =
serverController.getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", ""); connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode); connData.pskKey = serverController.getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
connData.pskKey.replace("\n", ""); connData.pskKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -142,27 +149,29 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
"AllowedIPs = %3/32\n\n") "AllowedIPs = %3/32\n\n")
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP); .arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath, errorCode = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath,
libssh::ScpOverwriteMode::ScpAppendToExisting); libssh::ScpOverwriteMode::ScpAppendToExisting);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; 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);
errorCode = m_serverController->runScript( errorCode = serverController.runScript(
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container))); credentials, serverController.replaceVars(script, serverController.genVarsForScript(credentials, container)));
return connData; return connData;
} }
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode errorCode)
{ {
ServerController serverController(m_settings);
QString scriptData = amnezia::scriptData(m_configTemplate, container); QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = QString config = serverController.replaceVars(
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig)); scriptData, serverController.genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode); ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -192,16 +201,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
return QJsonDocument(jConfig).toJson(); return QJsonDocument(jConfig).toJson();
} }
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
QString &protocolConfigString) const bool isApiConfig, QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString; return protocolConfigString;
} }
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
QString &protocolConfigString) const bool isApiConfig, QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
@@ -12,8 +12,7 @@ class WireguardConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg, WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent = nullptr);
QObject *parent = nullptr);
struct ConnectionData struct ConnectionData
{ {
@@ -26,17 +25,19 @@ public:
QString port; QString port;
}; };
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString); QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString); QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData genClientKeys(); static ConnectionData genClientKeys();
private: private:
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode errorCode);
bool m_isAwg; bool m_isAwg;
QString m_serverConfigPath; QString m_serverConfigPath;
+9 -9
View File
@@ -8,26 +8,26 @@
#include "core/controllers/serverController.h" #include "core/controllers/serverController.h"
#include "core/scripts_registry.h" #include "core/scripts_registry.h"
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent) XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, QObject *parent) : ConfiguratorBase(settings, parent)
: ConfiguratorBase(settings, serverController, parent)
{ {
} }
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode) ErrorCode errorCode)
{ {
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container), ServerController serverController(m_settings);
m_serverController->genVarsForScript(credentials, container, containerConfig));
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
QString xrayPublicKey = QString xrayPublicKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode); serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
xrayPublicKey.replace("\n", ""); xrayPublicKey.replace("\n", "");
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode); QString xrayUuid = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
xrayUuid.replace("\n", ""); xrayUuid.replace("\n", "");
QString xrayShortId = QString xrayShortId = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
xrayShortId.replace("\n", ""); xrayShortId.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
+2 -2
View File
@@ -10,10 +10,10 @@ class XrayConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr); XrayConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode); ErrorCode errorCode);
}; };
#endif // XRAY_CONFIGURATOR_H #endif // XRAY_CONFIGURATOR_H
+6 -6
View File
@@ -321,7 +321,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
switch (container) { switch (container) {
case DockerContainer::WireGuard: return true; case DockerContainer::WireGuard: return true;
case DockerContainer::Awg: return true; case DockerContainer::Awg: return true;
// case DockerContainer::Cloak: return true; case DockerContainer::Cloak: return true;
default: return false; default: return false;
} }
} }
@@ -330,8 +330,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
{ {
switch (container) { switch (container) {
case DockerContainer::WireGuard: return tr("Low"); case DockerContainer::WireGuard: return tr("Low");
case DockerContainer::Awg: return tr("High"); case DockerContainer::Awg: return tr("Medium or High");
// case DockerContainer::Cloak: return tr("Extreme"); case DockerContainer::Cloak: return tr("Extreme");
default: return ""; default: return "";
} }
} }
@@ -341,8 +341,8 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
switch (container) { switch (container) {
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy."); 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::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
// case DockerContainer::Cloak: case DockerContainer::Cloak:
// return tr("Most VPN protocols are blocked. Recommended if other options are not working."); return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
default: return ""; default: return "";
} }
} }
@@ -352,7 +352,7 @@ int ContainerProps::easySetupOrder(DockerContainer container)
switch (container) { switch (container) {
case DockerContainer::WireGuard: return 3; case DockerContainer::WireGuard: return 3;
case DockerContainer::Awg: return 2; case DockerContainer::Awg: return 2;
// case DockerContainer::Cloak: return 1; case DockerContainer::Cloak: return 1;
default: return 0; default: return 0;
} }
} }
+49 -62
View File
@@ -5,9 +5,8 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QtConcurrent> #include <QtConcurrent>
#include "amnezia_application.h" #include "core/errorstrings.h"
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "version.h"
namespace namespace
{ {
@@ -21,10 +20,6 @@ namespace
constexpr char certificate[] = "certificate"; constexpr char certificate[] = "certificate";
constexpr char publicKey[] = "public_key"; constexpr char publicKey[] = "public_key";
constexpr char protocol[] = "protocol"; constexpr char protocol[] = "protocol";
constexpr char uuid[] = "installation_uuid";
constexpr char osVersion[] = "os_version";
constexpr char appVersion[] = "app_version";
} }
} }
@@ -32,7 +27,8 @@ ApiController::ApiController(QObject *parent) : QObject(parent)
{ {
} }
void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config) void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData,
QString &config)
{ {
if (protocol == configKey::cloak) { if (protocol == configKey::cloak) {
config.replace("<key>", "<key>\n"); config.replace("<key>", "<key>\n");
@@ -65,52 +61,50 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont
} else if (protocol == configKey::awg) { } else if (protocol == configKey::awg) {
obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey; obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
} }
obj[configKey::osVersion] = QSysInfo::productType();
obj[configKey::appVersion] = QString(APP_VERSION);
return obj; return obj;
} }
void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig) ErrorCode ApiController::updateServerConfigFromApi(QJsonObject &serverConfig)
{ {
#ifdef Q_OS_IOS QFutureWatcher<ErrorCode> watcher;
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
auto containerConfig = serverConfig.value(config_key::containers).toArray(); QFuture<ErrorCode> future = QtConcurrent::run([this, &serverConfig]() {
auto containerConfig = serverConfig.value(config_key::containers).toArray();
if (serverConfig.value(config_key::configVersion).toInt()) { if (serverConfig.value(config_key::configVersion).toInt()) {
QNetworkRequest request; QNetworkAccessManager manager;
request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
request.setUrl(endpoint);
QString protocol = serverConfig.value(configKey::protocol).toString(); QNetworkRequest request;
request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization",
"Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
request.setUrl(endpoint);
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); QString protocol = serverConfig.value(configKey::protocol).toString();
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); auto apiPayloadData = generateApiPayloadData(protocol);
apiPayload[configKey::uuid] = installationUuid;
QByteArray requestBody = QJsonDocument(apiPayload).toJson(); QByteArray requestBody = QJsonDocument(fillApiPayload(protocol, apiPayloadData)).toJson();
QNetworkReply *reply = amnApp->manager()->post(request, requestBody); // ?? QScopedPointer<QNetworkReply> reply;
reply.reset(manager.post(request, requestBody));
QEventLoop wait;
QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit);
wait.exec();
QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable {
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
QString contents = QString::fromUtf8(reply->readAll()); QString contents = QString::fromUtf8(reply->readAll());
QString data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString();
data.replace("vpn://", ""); data.replace("vpn://", "");
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray ba = QByteArray::fromBase64(data.toUtf8(),
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (ba.isEmpty()) { if (ba.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError); return ErrorCode::ApiConfigDownloadError;
return;
} }
QByteArray ba_uncompressed = qUncompress(ba); QByteArray ba_uncompressed = qUncompress(ba);
@@ -122,37 +116,30 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c
processApiConfig(protocol, apiPayloadData, configStr); processApiConfig(protocol, apiPayloadData, configStr);
QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); serverConfig.insert(config_key::dns1, apiConfig.value(config_key::dns1));
serverConfig[config_key::containers] = apiConfig.value(config_key::containers); serverConfig.insert(config_key::dns2, apiConfig.value(config_key::dns2));
serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); serverConfig.insert(config_key::containers, apiConfig.value(config_key::containers));
serverConfig.insert(config_key::hostName, apiConfig.value(config_key::hostName));
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString(); auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer; serverConfig.insert(config_key::defaultContainer, defaultContainer);
emit configUpdated(true, serverConfig, serverIndex);
} else { } else {
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError QString err = reply->errorString();
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) { qDebug() << QString::fromUtf8(reply->readAll());
emit errorOccurred(ErrorCode::ApiConfigTimeoutError); qDebug() << reply->error();
} else { qDebug() << err;
QString err = reply->errorString(); qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qDebug() << QString::fromUtf8(reply->readAll()); return ErrorCode::ApiConfigDownloadError;
qDebug() << reply->error();
qDebug() << err;
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
emit errorOccurred(ErrorCode::ApiConfigDownloadError);
}
} }
}
return ErrorCode::NoError;
});
reply->deleteLater(); QEventLoop wait;
}); connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future);
wait.exec();
QObject::connect(reply, &QNetworkReply::errorOccurred, return watcher.result();
[this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; });
connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList<QSslError> &errors) {
qDebug().noquote() << errors;
emit errorOccurred(ErrorCode::ApiConfigSslError);
});
}
} }
+2 -11
View File
@@ -5,10 +5,6 @@
#include "configurators/openvpn_configurator.h" #include "configurators/openvpn_configurator.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
class ApiController : public QObject class ApiController : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -17,15 +13,10 @@ public:
explicit ApiController(QObject *parent = nullptr); explicit ApiController(QObject *parent = nullptr);
public slots: public slots:
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); ErrorCode updateServerConfigFromApi(QJsonObject &serverConfig);
signals:
void errorOccurred(ErrorCode errorCode);
void configUpdated(const bool updateConfig, const QJsonObject &config, const int serverIndex);
private: private:
struct ApiPayloadData struct ApiPayloadData {
{
OpenVpnConfigurator::ConnectionData certRequest; OpenVpnConfigurator::ConnectionData certRequest;
QString wireGuardClientPrivKey; QString wireGuardClientPrivKey;
+120 -68
View File
@@ -23,10 +23,10 @@
#include <thread> #include <thread>
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "core/networkUtilities.h" #include "logger.h"
#include "core/scripts_registry.h" #include "core/scripts_registry.h"
#include "core/server_defs.h" #include "core/server_defs.h"
#include "logger.h" #include "core/networkUtilities.h"
#include "settings.h" #include "settings.h"
#include "utilities.h" #include "utilities.h"
#include "vpnConfigurationController.h" #include "vpnConfigurationController.h"
@@ -95,9 +95,10 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, ErrorCode
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut, ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr) 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"; QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script); Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
@@ -115,8 +116,9 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
return e; return e;
} }
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file, ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
const QString &path, libssh::ScpOverwriteMode overwriteMode) const QString &file, const QString &path,
libssh::ScpOverwriteMode overwriteMode)
{ {
ErrorCode e = ErrorCode::NoError; ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
@@ -154,10 +156,12 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
if (e) if (e)
return e; return e;
e = runScript(credentials, e = runScript(
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path), credentials,
genVarsForScript(credentials, container)), replaceVars(
cbReadStd, cbReadStd); QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e) if (e)
return e; return e;
@@ -168,17 +172,20 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError; 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; return e;
} }
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
ErrorCode &errorCode) const QString &path, ErrorCode 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; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -190,8 +197,8 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
return QByteArray::fromHex(stdOut.toUtf8()); return QByteArray::fromHex(stdOut.toUtf8());
} }
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
libssh::ScpOverwriteMode overwriteMode) const QString &remotePath, libssh::ScpOverwriteMode overwriteMode)
{ {
auto error = m_sshClient.connectToHost(credentials); auto error = m_sshClient.connectToHost(credentials);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
@@ -237,10 +244,12 @@ ErrorCode ServerController::removeAllContainers(const ServerCredentials &credent
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container) ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
{ {
return runScript(credentials, 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); qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
ErrorCode e = ErrorCode::NoError; ErrorCode e = ErrorCode::NoError;
@@ -300,11 +309,12 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
return startupContainerWorker(credentials, container, config); return startupContainerWorker(credentials, container, config);
} }
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig, ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container,
QJsonObject &newConfig) const QJsonObject &oldConfig, QJsonObject &newConfig)
{ {
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, 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) { if (reinstallRequired) {
return setupContainer(credentials, container, newConfig, true); return setupContainer(credentials, container, newConfig, true);
@@ -317,7 +327,8 @@ 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); Proto mainProto = ContainerProps::defaultProtocol(container);
@@ -348,7 +359,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
if (container == DockerContainer::Awg) { if (container == DockerContainer::Awg) {
if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort) if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
!= newProtoConfig.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) || (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
!= newProtoConfig.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) || (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
@@ -370,7 +381,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
return true; return true;
} }
if (container == DockerContainer::WireGuard) { if (container == DockerContainer::WireGuard){
if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)) != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))
return true; return true;
@@ -396,7 +407,8 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
}; };
ErrorCode error = ErrorCode error =
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)), runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr); cbReadStdOut, cbReadStdErr);
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut; qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
@@ -408,13 +420,17 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
return error; 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 // 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(), ErrorCode e = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),
amnezia::server::getDockerfileFolder(container) + "/Dockerfile"); amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
@@ -429,7 +445,8 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
}; };
e = runScript(credentials, e = runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), replaceVars(amnezia::scriptData(SharedScriptType::build_container),
genVarsForScript(credentials, container, config)),
cbReadStdOut); cbReadStdOut);
if (e) if (e)
return e; return e;
@@ -437,7 +454,8 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
return e; return e;
} }
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container,
QJsonObject &config)
{ {
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -460,7 +478,8 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
return e; return e;
} }
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
QJsonObject &config)
{ {
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -482,7 +501,8 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
return e; 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); QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
@@ -490,7 +510,8 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
return ErrorCode::NoError; 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"); "/opt/amnezia/start.sh");
if (e) if (e)
return e; return e;
@@ -501,8 +522,8 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
genVarsForScript(credentials, container, config))); genVarsForScript(credentials, container, config)));
} }
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container, ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials,
const QJsonObject &config) DockerContainer container, const QJsonObject &config)
{ {
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject(); const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject(); const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
@@ -517,19 +538,24 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$REMOTE_HOST", credentials.hostName } }); vars.append({ { "$REMOTE_HOST", credentials.hostName } });
// OpenVPN vars // OpenVPN vars
vars.append({ { "$OPENVPN_SUBNET_IP", vars.append(
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } }); { { "$OPENVPN_SUBNET_IP",
vars.append({ { "$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } }); openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
vars.append({ { "$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } }); 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_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
vars.append({ { "$OPENVPN_TRANSPORT_PROTO", vars.append(
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } }); { { "$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); bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } }); 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) } }); 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); bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
@@ -540,35 +566,43 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
} }
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG", 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", 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 // 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", vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } }); 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({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } }); vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
// Cloak vars // Cloak vars
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } }); 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 // Xray vars
vars.append({ { "$XRAY_SITE_NAME", xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } }); vars.append({ { "$XRAY_SITE_NAME",
xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
// Wireguard vars // Wireguard vars
vars.append({ { "$WIREGUARD_SUBNET_IP", vars.append(
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } }); { { "$WIREGUARD_SUBNET_IP",
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
vars.append({ { "$WIREGUARD_SUBNET_CIDR", vars.append({ { "$WIREGUARD_SUBNET_CIDR",
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } }); wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
vars.append({ { "$WIREGUARD_SUBNET_MASK", vars.append({ { "$WIREGUARD_SUBNET_MASK",
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } }); 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 // IPsec vars
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } }); vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
@@ -591,22 +625,30 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } }); vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } });
// Sftp vars // 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_USER", sftpConfig.value(config_key::userName).toString() } });
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } }); vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
// Amnezia wireguard vars // 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_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).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({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).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({ { "$RESPONSE_PACKET_JUNK_SIZE",
vars.append({ { "$INIT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } }); amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } }); vars.append({ { "$INIT_PACKET_MAGIC_HEADER",
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } }); amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).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 = NetworkUtilities::getIPAddress(credentials.hostName); QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
if (!serverIp.isEmpty()) { if (!serverIp.isEmpty()) {
@@ -618,7 +660,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
return vars; return vars;
} }
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode &errorCode) QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode errorCode)
{ {
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -642,7 +684,9 @@ void ServerController::cancelInstallation()
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) 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) QString ServerController::replaceVars(const QString &script, const Vars &vars)
@@ -654,7 +698,8 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
return s; 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) { if (container == DockerContainer::Dns) {
return ErrorCode::NoError; return ErrorCode::NoError;
@@ -677,12 +722,15 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container); QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
QString defaultPort("%1"); QString defaultPort("%1");
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol))); QString port =
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol); 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); QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
// TODO reimplement with netstat // 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) { for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port); script = script.append("|:%1").arg(port);
} }
@@ -692,7 +740,8 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
script = script.append(" | grep LISTEN"); 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) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
@@ -720,7 +769,8 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
}; };
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo); 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")) if (!stdOut.contains("sudo"))
return ErrorCode::ServerUserNotInSudo; return ErrorCode::ServerUserNotInSudo;
@@ -750,7 +800,9 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
return ErrorCode::ServerCancelInstallation; return ErrorCode::ServerCancelInstallation;
} }
stdOut.clear(); 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); cbReadStdOut, cbReadStdErr);
if (stdOut.contains("Packet manager not found")) if (stdOut.contains("Packet manager not found"))
+22 -16
View File
@@ -25,18 +25,19 @@ public:
ErrorCode rebootServer(const ServerCredentials &credentials); ErrorCode rebootServer(const ServerCredentials &credentials);
ErrorCode removeAllContainers(const ServerCredentials &credentials); ErrorCode removeAllContainers(const ServerCredentials &credentials);
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container); ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate = false); ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config,
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig, bool isUpdate = false);
QJsonObject &newConfig); ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &oldConfig, QJsonObject &newConfig);
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config = QJsonObject()); const QJsonObject &config = QJsonObject());
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file, ErrorCode uploadTextFileToContainer(
const QString &path, DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path,
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting); libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
ErrorCode &errorCode); const QString &path, ErrorCode errorCode);
QString replaceVars(const QString &script, const Vars &vars); QString replaceVars(const QString &script, const Vars &vars);
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
@@ -46,11 +47,12 @@ public:
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr, const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr); const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, ErrorCode
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr, runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr); 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); QString checkSshConnection(const ServerCredentials &credentials, ErrorCode errorCode);
void cancelInstallation(); void cancelInstallation();
@@ -59,14 +61,18 @@ public:
private: private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); 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, ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config = QJsonObject()); const QJsonObject &config = QJsonObject());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); 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); ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); const QJsonObject &config);
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
const QJsonObject &newConfig);
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container); ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container); ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
@@ -8,22 +8,21 @@
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "configurators/xray_configurator.h" #include "configurators/xray_configurator.h"
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings, VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QObject *parent)
QSharedPointer<ServerController> serverController, QObject *parent) : QObject { parent }, m_settings(settings)
: QObject { parent }, m_settings(settings), m_serverController(serverController)
{ {
} }
QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator(const Proto protocol) QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator(const Proto protocol)
{ {
switch (protocol) { switch (protocol) {
case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(m_settings, m_serverController)); case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(m_settings));
case Proto::ShadowSocks: return QScopedPointer<ConfiguratorBase>(new ShadowSocksConfigurator(m_settings, m_serverController)); case Proto::ShadowSocks: return QScopedPointer<ConfiguratorBase>(new ShadowSocksConfigurator(m_settings));
case Proto::Cloak: return QScopedPointer<ConfiguratorBase>(new CloakConfigurator(m_settings, m_serverController)); case Proto::Cloak: return QScopedPointer<ConfiguratorBase>(new CloakConfigurator(m_settings));
case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(m_settings, m_serverController, false)); case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(m_settings, false));
case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(m_settings, m_serverController)); case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(m_settings));
case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(m_settings, m_serverController)); case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(m_settings));
case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings, m_serverController)); case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings));
default: return QScopedPointer<ConfiguratorBase>(); default: return QScopedPointer<ConfiguratorBase>();
} }
} }
@@ -77,7 +76,7 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig, QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container, const QJsonObject &containerConfig, const DockerContainer container,
ErrorCode &errorCode) ErrorCode errorCode)
{ {
QJsonObject vpnConfiguration {}; QJsonObject vpnConfiguration {};
@@ -12,7 +12,7 @@ class VpnConfigurationsController : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController, QObject *parent = nullptr); explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
public slots: public slots:
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container, ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
@@ -21,7 +21,7 @@ public slots:
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol, const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString); QString &protocolConfigString);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig, QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container, ErrorCode &errorCode); const QJsonObject &containerConfig, const DockerContainer container, ErrorCode errorCode);
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut); static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
signals: signals:
@@ -30,7 +30,6 @@ private:
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol); QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;
}; };
#endif // VPNCONFIGIRATIONSCONTROLLER_H #endif // VPNCONFIGIRATIONSCONTROLLER_H
+3 -15
View File
@@ -36,17 +36,12 @@ namespace amnezia
} }
}; };
namespace error_code_ns enum ErrorCode {
{
Q_NAMESPACE
// TODO: change to enum class
enum ErrorCode {
// General error codes // General error codes
NoError = 0, NoError = 0,
UnknownError = 100, UnknownError = 100,
InternalError = 101, InternalError = 101,
NotImplementedError = 102, NotImplementedError = 102,
AmneziaServiceNotRunning = 103,
// Server errors // Server errors
ServerCheckFailed = 200, ServerCheckFailed = 200,
@@ -79,7 +74,7 @@ namespace amnezia
AmneziaServiceConnectionFailed = 603, AmneziaServiceConnectionFailed = 603,
ExecutableMissing = 604, ExecutableMissing = 604,
XrayExecutableMissing = 605, XrayExecutableMissing = 605,
Tun2SockExecutableMissing = 606, Tun2SockExecutableMissing = 606,
// VPN errors // VPN errors
OpenVpnAdaptersInUseError = 700, OpenVpnAdaptersInUseError = 700,
@@ -103,9 +98,6 @@ namespace amnezia
// Api errors // Api errors
ApiConfigDownloadError = 1100, ApiConfigDownloadError = 1100,
ApiConfigAlreadyAdded = 1101, ApiConfigAlreadyAdded = 1101,
ApiConfigEmptyError = 1102,
ApiConfigTimeoutError = 1103,
ApiConfigSslError = 1104,
// QFile errors // QFile errors
OpenError = 1200, OpenError = 1200,
@@ -114,11 +106,7 @@ namespace amnezia
UnspecifiedError = 1203, UnspecifiedError = 1203,
FatalError = 1204, FatalError = 1204,
AbortError = 1205 AbortError = 1205
}; };
Q_ENUM_NS(ErrorCode)
}
using ErrorCode = error_code_ns::ErrorCode;
} // namespace amnezia } // namespace amnezia
+40 -44
View File
@@ -8,68 +8,64 @@ QString errorString(ErrorCode code) {
switch (code) { switch (code) {
// General error codes // General error codes
case(ErrorCode::NoError): errorMessage = QObject::tr("No error"); break; case(NoError): errorMessage = QObject::tr("No error"); break;
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown Error"); break; case(UnknownError): errorMessage = QObject::tr("Unknown Error"); break;
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break; case(NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
// Server errors // Server errors
case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break; case(ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break;
case(ErrorCode::ServerPortAlreadyAllocatedError): errorMessage = QObject::tr("Server port already used. Check for another software"); break; case(ServerPortAlreadyAllocatedError): errorMessage = QObject::tr("Server port already used. Check for another software"); break;
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break; case(ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break; case(ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; case(ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break; case(ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break; case(ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
// Libssh errors // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break; case(SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break;
case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break; case(SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break;
case(ErrorCode::SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break; case(SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break;
case(ErrorCode::SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break; case(SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break;
case(ErrorCode::SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break; case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break;
case(ErrorCode::SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break; case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break;
// Ssh scp errors // Ssh scp errors
case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break; case(SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break;
// Local errors // Local errors
case (ErrorCode::OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break; case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break;
case (ErrorCode::OpenVpnManagementServerError): errorMessage = QObject::tr("OpenVPN management server error"); break; case (OpenVpnManagementServerError): errorMessage = QObject::tr("OpenVPN management server error"); break;
// Distro errors // Distro errors
case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break; case (OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break;
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break; case (ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break;
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break; case (CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break; case (AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break; case (OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
// VPN errors // VPN errors
case (ErrorCode::OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break; case (OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break;
case (ErrorCode::OpenVpnTapAdapterError): errorMessage = QObject::tr("Can't setup OpenVPN TAP network adapter"); break; case (OpenVpnTapAdapterError): errorMessage = QObject::tr("Can't setup OpenVPN TAP network adapter"); break;
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; case (AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break;
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; case (ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
// Android errors // Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break; case (AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
// Api errors // Api errors
case (ErrorCode::ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ErrorCode::ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break; case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break;
case (ErrorCode::ApiConfigEmptyError): errorMessage = QObject::tr("In the response from the server, an empty config was received"); break;
case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break;
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
case(ErrorCode::ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
case(ErrorCode::PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break;
case(ErrorCode::UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break;
case(ErrorCode::FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break;
case(ErrorCode::AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break;
case(ErrorCode::InternalError): // QFile errors
case(OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
case(ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
case(PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break;
case(UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break;
case(FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break;
case(AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break;
case(InternalError):
default: default:
errorMessage = QObject::tr("Internal error"); break; errorMessage = QObject::tr("Internal error"); break;
} }
-7
View File
@@ -23,13 +23,6 @@ namespace libssh {
ErrorCode Client::connectToHost(const ServerCredentials &credentials) 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) { if (m_session == nullptr) {
m_session = ssh_new(); m_session = ssh_new();
+6 -21
View File
@@ -248,10 +248,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
GETVALUE("privateKey", config.m_privateKey, String); GETVALUE("privateKey", config.m_privateKey, String);
GETVALUE("serverPublicKey", config.m_serverPublicKey, String); GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
GETVALUE("serverPskKey", config.m_serverPskKey, String);
GETVALUE("serverPort", config.m_serverPort, Double); GETVALUE("serverPort", config.m_serverPort, Double);
config.m_serverPskKey = obj.value("serverPskKey").toString();
if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0) if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0)
{ {
config.m_deviceMTU = 1420; config.m_deviceMTU = 1420;
@@ -374,33 +373,19 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
return false; return false;
} }
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool(); if (!obj.value("Jc").isNull() && !obj.value("Jmin").isNull()
&& !obj.value("Jmax").isNull() && !obj.value("S1").isNull()
if (!obj.value("Jc").isNull()) { && !obj.value("S2").isNull() && !obj.value("H1").isNull()
&& !obj.value("H2").isNull() && !obj.value("H3").isNull()
&& !obj.value("H4").isNull()) {
config.m_junkPacketCount = obj.value("Jc").toString(); config.m_junkPacketCount = obj.value("Jc").toString();
}
if (!obj.value("Jmin").isNull()) {
config.m_junkPacketMinSize = obj.value("Jmin").toString(); config.m_junkPacketMinSize = obj.value("Jmin").toString();
}
if (!obj.value("Jmax").isNull()) {
config.m_junkPacketMaxSize = obj.value("Jmax").toString(); config.m_junkPacketMaxSize = obj.value("Jmax").toString();
}
if (!obj.value("S1").isNull()) {
config.m_initPacketJunkSize = obj.value("S1").toString(); config.m_initPacketJunkSize = obj.value("S1").toString();
}
if (!obj.value("S2").isNull()) {
config.m_responsePacketJunkSize = obj.value("S2").toString(); config.m_responsePacketJunkSize = obj.value("S2").toString();
}
if (!obj.value("H1").isNull()) {
config.m_initPacketMagicHeader = obj.value("H1").toString(); config.m_initPacketMagicHeader = obj.value("H1").toString();
}
if (!obj.value("H2").isNull()) {
config.m_responsePacketMagicHeader = obj.value("H2").toString(); config.m_responsePacketMagicHeader = obj.value("H2").toString();
}
if (!obj.value("H3").isNull()) {
config.m_underloadPacketMagicHeader = obj.value("H3").toString(); config.m_underloadPacketMagicHeader = obj.value("H3").toString();
}
if (!obj.value("H4").isNull()) {
config.m_transportPacketMagicHeader = obj.value("H4").toString(); config.m_transportPacketMagicHeader = obj.value("H4").toString();
} }
-1
View File
@@ -37,7 +37,6 @@ class InterfaceConfig {
QList<IPAddress> m_allowedIPAddressRanges; QList<IPAddress> m_allowedIPAddressRanges;
QStringList m_excludedAddresses; QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps; QStringList m_vpnDisabledApps;
bool m_killSwitchEnabled;
#if defined(MZ_ANDROID) || defined(MZ_IOS) #if defined(MZ_ANDROID) || defined(MZ_IOS)
QString m_installationId; QString m_installationId;
#endif #endif
-7
View File
@@ -51,13 +51,6 @@
<true/> <true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string> <string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>CFBundleIcons</key> <key>CFBundleIcons</key>
<dict/> <dict/>
<key>CFBundleIcons~ipad</key> <key>CFBundleIcons~ipad</key>
-1
View File
@@ -64,7 +64,6 @@ int main(int argc, char *argv[])
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH); qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
return app.exec(); return app.exec();
} }
+1 -21
View File
@@ -221,9 +221,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("excludedAddresses", jsExcludedAddresses); json.insert("excludedAddresses", jsExcludedAddresses);
json.insert("vpnDisabledApps", splitTunnelApps); json.insert("vpnDisabledApps", splitTunnelApps);
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
if (protocolName == amnezia::config_key::awg) { if (protocolName == amnezia::config_key::awg) {
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount)); 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::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
@@ -234,24 +232,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); 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::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); 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); write(json);
+5 -11
View File
@@ -171,15 +171,9 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName,
} }
QNetworkInformation::Reachability NetworkWatcher::getReachability() { QString NetworkWatcher::getCurrentTransport() {
if (m_simulatedDisconnection) { auto type = m_impl->getTransportType();
return QNetworkInformation::Reachability::Disconnected; QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
} else if (QNetworkInformation::instance()) { return QString(metaEnum.valueToKey(type))
return QNetworkInformation::instance()->reachability(); .remove("TransportType_", Qt::CaseSensitive);
}
return QNetworkInformation::Reachability::Unknown;
}
void NetworkWatcher::simulateDisconnection(bool simulatedDisconnection) {
m_simulatedDisconnection = simulatedDisconnection;
} }
+23 -28
View File
@@ -7,50 +7,45 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QMap> #include <QMap>
#include <QNetworkInformation> #include <QObject>
class NetworkWatcherImpl; class NetworkWatcherImpl;
// This class watches for network changes to detect unsecured wifi. // This class watches for network changes to detect unsecured wifi.
class NetworkWatcher final : public QObject { class NetworkWatcher final : public QObject {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(NetworkWatcher) Q_DISABLE_COPY_MOVE(NetworkWatcher)
public: public:
NetworkWatcher(); NetworkWatcher();
~NetworkWatcher(); ~NetworkWatcher();
void initialize(); void initialize();
// Public for the Inspector. // public for the inspector.
void unsecuredNetwork(const QString& networkName, const QString& networkId); 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);
QNetworkInformation::Reachability getReachability(); QString getCurrentTransport();
signals: signals:
void networkChange(); void networkChange();
private: private:
void settingsChanged(); void settingsChanged();
private: // void notificationClicked(NotificationHandler::Message message);
bool m_active = false;
bool m_reportUnsecuredNetwork = false;
// Platform-specific implementation. private:
NetworkWatcherImpl* m_impl = nullptr; bool m_active = false;
bool m_reportUnsecuredNetwork = false;
QMap<QString, QElapsedTimer> m_networks; // Platform-specific implementation.
NetworkWatcherImpl* m_impl = nullptr;
// This is used to connect NotificationHandler lazily. QMap<QString, QElapsedTimer> m_networks;
bool m_firstNotification = true;
// Used to simulate network disconnection in the Inspector // This is used to connect NotificationHandler lazily.
bool m_simulatedDisconnection = false; bool m_firstNotification = true;
}; };
#endif // NETWORKWATCHER_H #endif // NETWORKWATCHER_H
+33 -28
View File
@@ -5,45 +5,50 @@
#ifndef NETWORKWATCHERIMPL_H #ifndef NETWORKWATCHERIMPL_H
#define NETWORKWATCHERIMPL_H #define NETWORKWATCHERIMPL_H
#include <QNetworkInformation>
#include <QObject> #include <QObject>
class NetworkWatcherImpl : public QObject { class NetworkWatcherImpl : public QObject {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl) Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
public: public:
NetworkWatcherImpl(QObject* parent) : QObject(parent) {} 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 start() { m_active = true; }
virtual void stop() { m_active = false; } virtual void stop() { m_active = false; }
bool isActive() const { return m_active; } bool isActive() const { return m_active; }
enum TransportType { enum TransportType {
TransportType_Unknown = 0, TransportType_Unknown = 0,
TransportType_Ethernet = 1, TransportType_Ethernet = 1,
TransportType_WiFi = 2, TransportType_WiFi = 2,
TransportType_Cellular = 3, // In Case the API does not retun the gsm type TransportType_Cellular = 3, // In Case the API does not retun the gsm type
TransportType_Other = 4, // I.e USB thethering TransportType_Other = 4, // I.e USB thethering
TransportType_None = 5 // I.e Airplane Mode or no active network device TransportType_None = 5 // I.e Airplane Mode or no active network device
}; };
Q_ENUM(TransportType); Q_ENUM(TransportType);
signals: // Returns the current type of Network Connection
// Fires when the Device Connects to an unsecured Network virtual TransportType getTransportType() = 0;
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);
private: signals:
bool m_active = false; // 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;
}; };
#endif // NETWORKWATCHERIMPL_H #endif // NETWORKWATCHERIMPL_H
@@ -93,7 +93,6 @@ bool AndroidController::initialize()
{"onServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected)}, {"onServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected)},
{"onServiceError", "()V", reinterpret_cast<void *>(onServiceError)}, {"onServiceError", "()V", reinterpret_cast<void *>(onServiceError)},
{"onVpnPermissionRejected", "()V", reinterpret_cast<void *>(onVpnPermissionRejected)}, {"onVpnPermissionRejected", "()V", reinterpret_cast<void *>(onVpnPermissionRejected)},
{"onNotificationStateChanged", "()V", reinterpret_cast<void *>(onNotificationStateChanged)},
{"onVpnStateChanged", "(I)V", reinterpret_cast<void *>(onVpnStateChanged)}, {"onVpnStateChanged", "(I)V", reinterpret_cast<void *>(onVpnStateChanged)},
{"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)}, {"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)},
{"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)}, {"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)},
@@ -136,7 +135,7 @@ ErrorCode AndroidController::start(const QJsonObject &vpnConfig)
callActivityMethod("start", "(Ljava/lang/String;)V", callActivityMethod("start", "(Ljava/lang/String;)V",
QJniObject::fromString(config).object<jstring>()); QJniObject::fromString(config).object<jstring>());
return ErrorCode::NoError; return NoError;
} }
void AndroidController::stop() void AndroidController::stop()
@@ -174,6 +173,14 @@ QString AndroidController::openFile(const QString &filter)
return fileName; return fileName;
} }
void AndroidController::setNotificationText(const QString &title, const QString &message, int timerSec)
{
callActivityMethod("setNotificationText", "(Ljava/lang/String;Ljava/lang/String;I)V",
QJniObject::fromString(title).object<jstring>(),
QJniObject::fromString(message).object<jstring>(),
(jint) timerSec);
}
bool AndroidController::isCameraPresent() bool AndroidController::isCameraPresent()
{ {
return callActivityMethod<jboolean>("isCameraPresent", "()Z"); return callActivityMethod<jboolean>("isCameraPresent", "()Z");
@@ -250,16 +257,6 @@ QPixmap AndroidController::getAppIcon(const QString &package, QSize *size, const
return QPixmap::fromImage(image); return QPixmap::fromImage(image);
} }
bool AndroidController::isNotificationPermissionGranted()
{
return callActivityMethod<jboolean>("isNotificationPermissionGranted", "()Z");
}
void AndroidController::requestNotificationPermission()
{
callActivityMethod("requestNotificationPermission", "()V");
}
// Moving log processing to the Android side // Moving log processing to the Android side
jclass AndroidController::log; jclass AndroidController::log;
jmethodID AndroidController::logDebug; jmethodID AndroidController::logDebug;
@@ -412,15 +409,6 @@ void AndroidController::onVpnPermissionRejected(JNIEnv *env, jobject thiz)
emit AndroidController::instance()->vpnPermissionRejected(); emit AndroidController::instance()->vpnPermissionRejected();
} }
// static
void AndroidController::onNotificationStateChanged(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->notificationStateChanged();
}
// static // static
void AndroidController::onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode) void AndroidController::onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode)
{ {
@@ -32,6 +32,7 @@ public:
ErrorCode start(const QJsonObject &vpnConfig); ErrorCode start(const QJsonObject &vpnConfig);
void stop(); void stop();
void resetLastServer(int serverIndex); void resetLastServer(int serverIndex);
void setNotificationText(const QString &title, const QString &message, int timerSec);
void saveFile(const QString &fileName, const QString &data); void saveFile(const QString &fileName, const QString &data);
QString openFile(const QString &filter); QString openFile(const QString &filter);
bool isCameraPresent(); bool isCameraPresent();
@@ -43,8 +44,6 @@ public:
void minimizeApp(); void minimizeApp();
QJsonArray getAppList(); QJsonArray getAppList();
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize); QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
bool isNotificationPermissionGranted();
void requestNotificationPermission();
static bool initLogging(); static bool initLogging();
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
@@ -55,7 +54,6 @@ signals:
void serviceDisconnected(); void serviceDisconnected();
void serviceError(); void serviceError();
void vpnPermissionRejected(); void vpnPermissionRejected();
void notificationStateChanged();
void vpnStateChanged(ConnectionState state); void vpnStateChanged(ConnectionState state);
void statisticsUpdated(quint64 rxBytes, quint64 txBytes); void statisticsUpdated(quint64 rxBytes, quint64 txBytes);
void fileOpened(QString uri); void fileOpened(QString uri);
@@ -83,7 +81,6 @@ private:
static void onServiceDisconnected(JNIEnv *env, jobject thiz); static void onServiceDisconnected(JNIEnv *env, jobject thiz);
static void onServiceError(JNIEnv *env, jobject thiz); static void onServiceError(JNIEnv *env, jobject thiz);
static void onVpnPermissionRejected(JNIEnv *env, jobject thiz); static void onVpnPermissionRejected(JNIEnv *env, jobject thiz);
static void onNotificationStateChanged(JNIEnv *env, jobject thiz);
static void onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode); static void onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode);
static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes);
static void onConfigImported(JNIEnv *env, jobject thiz, jstring data); static void onConfigImported(JNIEnv *env, jobject thiz, jstring data);
@@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "android_notificationhandler.h"
#include "platforms/android/android_controller.h"
AndroidNotificationHandler::AndroidNotificationHandler(QObject* parent)
: NotificationHandler(parent) {
}
AndroidNotificationHandler::~AndroidNotificationHandler() {
}
void AndroidNotificationHandler::notify(NotificationHandler::Message type,
const QString& title,
const QString& message, int timerMsec) {
Q_UNUSED(type);
qDebug() << "Send notification - " << message;
AndroidController::instance()->setNotificationText(title, message,
timerMsec / 1000);
}
@@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ANDROIDNOTIFICATIONHANDLER_H
#define ANDROIDNOTIFICATIONHANDLER_H
#include "ui/notificationhandler.h"
#include <QObject>
class AndroidNotificationHandler final : public NotificationHandler {
Q_DISABLE_COPY_MOVE(AndroidNotificationHandler)
public:
AndroidNotificationHandler(QObject* parent);
~AndroidNotificationHandler();
protected:
void notify(Message type, const QString& title, const QString& message,
int timerMsec) override;
};
#endif // ANDROIDNOTIFICATIONHANDLER_H
@@ -8,11 +8,11 @@
DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent) DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent)
: NetworkWatcherImpl(parent) { : NetworkWatcherImpl(parent) {
MZ_COUNT_CTOR(DummyNetworkWatcher); MZ_COUNT_CTOR(DummyNetworkWatcher);
} }
DummyNetworkWatcher::~DummyNetworkWatcher() { DummyNetworkWatcher::~DummyNetworkWatcher() {
MZ_COUNT_DTOR(DummyNetworkWatcher); MZ_COUNT_DTOR(DummyNetworkWatcher);
} }
void DummyNetworkWatcher::initialize() {} void DummyNetworkWatcher::initialize() {}
+8 -4
View File
@@ -8,11 +8,15 @@
#include "networkwatcherimpl.h" #include "networkwatcherimpl.h"
class DummyNetworkWatcher final : public NetworkWatcherImpl { class DummyNetworkWatcher final : public NetworkWatcherImpl {
public: public:
DummyNetworkWatcher(QObject* parent); DummyNetworkWatcher(QObject* parent);
~DummyNetworkWatcher(); ~DummyNetworkWatcher();
void initialize() override; void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override {
return TransportType_Other;
};
}; };
#endif // DUMMYNETWORKWATCHER_H #endif // DUMMYNETWORKWATCHER_H
-13
View File
@@ -16,11 +16,6 @@ struct Log {
private static let appGroupID = "group.org.amnezia.AmneziaVPN" 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 = { static let neLogURL = {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)! let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)!
return sharedContainerURL.appendingPathComponent("ne.log", isDirectory: false) return sharedContainerURL.appendingPathComponent("ne.log", isDirectory: false)
@@ -75,12 +70,8 @@ struct Log {
} }
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) { static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
NSLog("\(title) \(message)")
guard isLoggingEnabled else { return } guard isLoggingEnabled else { return }
osLog.log(level: type, "\(title) \(message)")
let date = Date() let date = Date()
let level = Record.Level(from: type) let level = Record.Level(from: type)
let messages = message.split(whereSeparator: \.isNewline) let messages = message.split(whereSeparator: \.isNewline)
@@ -116,7 +107,3 @@ extension Log: CustomStringConvertible {
.joined(separator: "\n") .joined(separator: "\n")
} }
} }
func log(_ type: OSLogType, title: String = "", message: String) {
Log.log(type, title: "App: \(title)", message: message, url: Log.appLogURL)
}
+27 -10
View File
@@ -1,33 +1,50 @@
import Foundation import Foundation
import NetworkExtension
public func swiftUpdateLogData(_ qtString: std.string) -> std.string { public func swiftUpdateLogData(_ qtString: std.string) -> std.string {
let qtLog = Log(String(describing: qtString)) let qtLog = Log(String(describing: qtString))
var log = qtLog var log = qtLog
if let appLog = Log(at: Log.appLogURL) {
appLog.records.forEach {
log.records.append($0)
}
}
if let neLog = Log(at: Log.neLogURL) { if let neLog = Log(at: Log.neLogURL) {
neLog.records.forEach { neLog.records.forEach {
log.records.append($0) log.records.append($0)
} }
}
log.records.sort { log.records.sort {
$0.date < $1.date $0.date < $1.date
}
} }
return std.string(log.description) return std.string(log.description)
} }
public func swiftDeleteLog() { public func swiftDeleteLog() {
Log.clear(at: Log.appLogURL)
Log.clear(at: Log.neLogURL) Log.clear(at: Log.neLogURL)
} }
public func toggleLogging(_ isEnabled: Bool) { public func toggleLogging(_ isEnabled: Bool) {
Log.isLoggingEnabled = isEnabled 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)")
}
}
}
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
#include "MobileUtils.h"
MobileUtils::MobileUtils(QObject *parent) : QObject(parent)
{
}
bool MobileUtils::shareText(const QStringList &)
{
return false;
}
QString MobileUtils::openFile()
{
return QString();
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef MOBILEUTILS_H
#define MOBILEUTILS_H
#include <QObject>
#include <QStringList>
class MobileUtils : public QObject
{
Q_OBJECT
public:
explicit MobileUtils(QObject *parent = nullptr);
public slots:
bool shareText(const QStringList &filesToSend);
QString openFile();
signals:
void finished();
};
#endif // MOBILEUTILS_H
+109
View File
@@ -0,0 +1,109 @@
#include "MobileUtils.h"
#include <UIKit/UIKit.h>
#include <Security/Security.h>
#include <QEventLoop>
static UIViewController* getViewController() {
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
return window.rootViewController;
}
}
return nil;
}
MobileUtils::MobileUtils(QObject *parent) : QObject(parent) {
}
bool MobileUtils::shareText(const QStringList& filesToSend) {
NSMutableArray *sharingItems = [NSMutableArray new];
for (int i = 0; i < filesToSend.size(); i++) {
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
[sharingItems addObject:logFileUrl];
}
UIViewController *qtController = getViewController();
if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
__block bool isAccepted = false;
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed;
emit finished();
}];
[qtController presentViewController:activityController animated:YES completion:nil];
UIPopoverPresentationController *popController = activityController.popoverPresentationController;
if (popController) {
popController.sourceView = qtController.view;
popController.sourceRect = CGRectMake(100, 100, 100, 100);
}
QEventLoop wait;
QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit);
wait.exec();
return isAccepted;
}
typedef void (^DocumentPickerClosedCallback)(NSString *path);
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
@end
@implementation DocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
for (NSURL *url in urls) {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback([url path]);
}
}
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback(nil);
}
}
@end
QString MobileUtils::openFile() {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
documentPicker.delegate = documentPickerDelegate;
UIViewController *qtController = getViewController();
if (!qtController) return;
[qtController presentViewController:documentPicker animated:YES completion:nil];
__block QString filePath;
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
if (path) {
filePath = QString::fromUtf8(path.UTF8String);
} else {
filePath = QString();
}
emit finished();
};
QEventLoop wait;
QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit);
wait.exec();
return filePath;
}
@@ -94,8 +94,6 @@ extension PacketTunnelProvider {
} }
func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
ovpnLog(.info, message: "Stopping tunnel: reason: \(reason.description)")
stopHandler = completionHandler stopHandler = completionHandler
if vpnReachability.isTracking { if vpnReachability.isTracking {
vpnReachability.stopTracking() vpnReachability.stopTracking()
@@ -200,7 +200,7 @@ extension PacketTunnelProvider {
// } // }
func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, message: "Stopping tunnel: reason: \(reason.description)") wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile() ErrorNotifier.removeLastErrorFile()
@@ -200,46 +200,3 @@ 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"
}
}
}
-50
View File
@@ -1,50 +0,0 @@
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))")
}
}
-3
View File
@@ -86,9 +86,6 @@ struct WGConfig: Decodable {
AllowedIPs = \(allowedIPs.joined(separator: ", ")) AllowedIPs = \(allowedIPs.joined(separator: ", "))
Endpoint = \(hostName):\(port) Endpoint = \(hostName):\(port)
PersistentKeepalive = \(persistentKeepAlive) PersistentKeepalive = \(persistentKeepAlive)
SplitTunnelType = \(splitTunnelType)
SplitTunnelSites = \(splitTunnelSites.joined(separator: ", "))
""" """
} }
} }
-7
View File
@@ -50,19 +50,12 @@ public:
void getBackendLogs(std::function<void(const QString &)> &&callback); void getBackendLogs(std::function<void(const QString &)> &&callback);
void checkStatus(); void checkStatus();
bool shareText(const QStringList &filesToSend);
QString openFile();
void requestInetAccess();
signals: signals:
void connectionStateChanged(Vpn::ConnectionState state); void connectionStateChanged(Vpn::ConnectionState state);
void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
void importConfigFromOutside(const QString); void importConfigFromOutside(const QString);
void importBackupFromOutside(const QString); void importBackupFromOutside(const QString);
void finished();
protected slots: protected slots:
private: private:
+7 -103
View File
@@ -6,7 +6,6 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QThread> #include <QThread>
#include <QEventLoop>
#include "../protocols/vpnprotocol.h" #include "../protocols/vpnprotocol.h"
#import "ios_controller_wrapper.h" #import "ios_controller_wrapper.h"
@@ -27,15 +26,6 @@ const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType"; const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites"; const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
static UIViewController* getViewController() {
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
return window.rootViewController;
}
}
return nil;
}
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) { switch (status) {
@@ -99,11 +89,9 @@ bool IosController::initialize()
for (NETunnelProviderManager *manager in managers) { for (NETunnelProviderManager *manager in managers) {
qDebug() << "IosController::initialize : VPNC: " << manager.localizedDescription;
if (manager.connection.status == NEVPNStatusConnected) { if (manager.connection.status == NEVPNStatusConnected) {
m_currentTunnel = manager; m_currentTunnel = manager;
qDebug() << "IosController::initialize : VPN already connected with" << manager.localizedDescription; qDebug() << "IosController::initialize : VPN already connected";
emit connectionStateChanged(Vpn::ConnectionState::Connected); emit connectionStateChanged(Vpn::ConnectionState::Connected);
break; break;
@@ -150,7 +138,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) { [NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
@try { @try {
if (error) { if (error) {
qDebug() << "IosController::connectVpn : VPNC: loadAllFromPreferences error:" << [error.localizedDescription UTF8String]; qDebug() << "IosController::connectVpn : Error:" << [error.localizedDescription UTF8String];
emit connectionStateChanged(Vpn::ConnectionState::Error); emit connectionStateChanged(Vpn::ConnectionState::Error);
ok = false; ok = false;
return; return;
@@ -163,7 +151,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
for (NETunnelProviderManager *manager in managers) { for (NETunnelProviderManager *manager in managers) {
if ([manager.localizedDescription isEqualToString:tunnelName.toNSString()]) { if ([manager.localizedDescription isEqualToString:tunnelName.toNSString()]) {
m_currentTunnel = manager; m_currentTunnel = manager;
qDebug() << "IosController::connectVpn : Using existing tunnel:" << manager.localizedDescription; qDebug() << "IosController::connectVpn : Using existing tunnel";
if (manager.connection.status == NEVPNStatusConnected) { if (manager.connection.status == NEVPNStatusConnected) {
emit connectionStateChanged(Vpn::ConnectionState::Connected); emit connectionStateChanged(Vpn::ConnectionState::Connected);
return; return;
@@ -174,10 +162,10 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
} }
if (!m_currentTunnel) { if (!m_currentTunnel) {
qDebug() << "IosController::connectVpn : Creating new tunnel";
isNewTunnelCreated = true; isNewTunnelCreated = true;
m_currentTunnel = [[NETunnelProviderManager alloc] init]; m_currentTunnel = [[NETunnelProviderManager alloc] init];
m_currentTunnel.localizedDescription = [NSString stringWithUTF8String:tunnelName.toStdString().c_str()]; m_currentTunnel.localizedDescription = [NSString stringWithUTF8String:tunnelName.toStdString().c_str()];
qDebug() << "IosController::connectVpn : Creating new tunnel" << m_currentTunnel.localizedDescription;
} }
} }
@@ -610,14 +598,13 @@ void IosController::startTunnel()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (saveError) { if (saveError) {
qDebug().nospace() << "IosController::startTunnel" << protocolName << ": Connect " << protocolName << " Tunnel Save Error" << saveError.localizedDescription.UTF8String;
emit connectionStateChanged(Vpn::ConnectionState::Error); emit connectionStateChanged(Vpn::ConnectionState::Error);
return; return;
} }
[m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) { [m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) {
if (loadError) { if (loadError) {
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String; qDebug().nospace() << "IosController::start" << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String;
emit connectionStateChanged(Vpn::ConnectionState::Error); emit connectionStateChanged(Vpn::ConnectionState::Error);
return; return;
} }
@@ -628,11 +615,11 @@ void IosController::startTunnel()
BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError]; BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError];
if (!started || startError) { if (!started || startError) {
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Connect " << protocolName << " Tunnel Start Error" qDebug().nospace() << "IosController::start" << protocolName << " : Connect " << protocolName << " Tunnel Start Error"
<< (startError ? startError.localizedDescription.UTF8String : ""); << (startError ? startError.localizedDescription.UTF8String : "");
emit connectionStateChanged(Vpn::ConnectionState::Error); emit connectionStateChanged(Vpn::ConnectionState::Error);
} else { } else {
qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Starting the tunnel succeeded"; qDebug().nospace() << "IosController::start" << protocolName << " : Starting the tunnel succeeded";
} }
}]; }];
}); });
@@ -713,86 +700,3 @@ void IosController::sendVpnExtensionMessage(NSDictionary* message, std::function
} }
} }
bool IosController::shareText(const QStringList& filesToSend) {
NSMutableArray *sharingItems = [NSMutableArray new];
for (int i = 0; i < filesToSend.size(); i++) {
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
[sharingItems addObject:logFileUrl];
}
UIViewController *qtController = getViewController();
if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
__block bool isAccepted = false;
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed;
emit finished();
}];
[qtController presentViewController:activityController animated:YES completion:nil];
UIPopoverPresentationController *popController = activityController.popoverPresentationController;
if (popController) {
popController.sourceView = qtController.view;
popController.sourceRect = CGRectMake(100, 100, 100, 100);
}
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return isAccepted;
}
QString IosController::openFile() {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
documentPicker.delegate = documentPickerDelegate;
UIViewController *qtController = getViewController();
if (!qtController) return;
[qtController presentViewController:documentPicker animated:YES completion:nil];
__block QString filePath;
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
if (path) {
filePath = QString::fromUtf8(path.UTF8String);
} else {
filePath = QString();
}
emit finished();
};
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return filePath;
}
void IosController::requestInetAccess() {
NSURL *url = [NSURL URLWithString:@"http://captive.apple.com/generate_204"];
if (url) {
qDebug() << "IosController::requestInetAccess URL error";
return;
}
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
qDebug() << "IosController::requestInetAccess error:" << error.localizedDescription;
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
QString responseBody = QString::fromUtf8((const char*)data.bytes, data.length);
qDebug() << "IosController::requestInetAccess server response:" << httpResponse.statusCode << "\n\n" <<responseBody;
}
}];
[task resume];
}
@@ -1,8 +1,6 @@
#import <NetworkExtension/NetworkExtension.h> #import <NetworkExtension/NetworkExtension.h>
#import <NetworkExtension/NETunnelProviderSession.h> #import <NetworkExtension/NETunnelProviderSession.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
#include <Security/Security.h>
class IosController; class IosController;
@@ -15,11 +13,3 @@ class IosController;
- (void)vpnConfigurationDidChange:(NSNotification *)notification; - (void)vpnConfigurationDidChange:(NSNotification *)notification;
@end @end
typedef void (^DocumentPickerClosedCallback)(NSString *path);
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
@end
+1 -18
View File
@@ -24,22 +24,5 @@
// cppController->vpnStatusDidChange(notification); // cppController->vpnStatusDidChange(notification);
} }
@end @end
@implementation DocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
for (NSURL *url in urls) {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback([url path]);
}
}
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback(nil);
}
}
@end
+1
View File
@@ -15,6 +15,7 @@ class IOSNetworkWatcher : public NetworkWatcherImpl {
~IOSNetworkWatcher(); ~IOSNetworkWatcher();
void initialize() override; void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override;
private: private:
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path); NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);
+10
View File
@@ -37,6 +37,16 @@ void IOSNetworkWatcher::initialize() {
//TODO IMPL FOR AMNEZIA //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) { NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) {
if (path == nil) { if (path == nil) {
return NetworkWatcherImpl::TransportType_Unknown; return NetworkWatcherImpl::TransportType_Unknown;
@@ -103,32 +103,15 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
out << "private_key=" << QString(privateKey.toHex()) << "\n"; out << "private_key=" << QString(privateKey.toHex()) << "\n";
out << "replace_peers=true\n"; out << "replace_peers=true\n";
if (config.m_junkPacketCount != "") {
if (!config.m_junkPacketCount.isEmpty()) {
out << "jc=" << config.m_junkPacketCount << "\n"; out << "jc=" << config.m_junkPacketCount << "\n";
}
if (!config.m_junkPacketMinSize.isEmpty()) {
out << "jmin=" << config.m_junkPacketMinSize << "\n"; out << "jmin=" << config.m_junkPacketMinSize << "\n";
}
if (!config.m_junkPacketMaxSize.isEmpty()) {
out << "jmax=" << config.m_junkPacketMaxSize << "\n"; out << "jmax=" << config.m_junkPacketMaxSize << "\n";
}
if (!config.m_initPacketJunkSize.isEmpty()) {
out << "s1=" << config.m_initPacketJunkSize << "\n"; out << "s1=" << config.m_initPacketJunkSize << "\n";
}
if (!config.m_responsePacketJunkSize.isEmpty()) {
out << "s2=" << config.m_responsePacketJunkSize << "\n"; out << "s2=" << config.m_responsePacketJunkSize << "\n";
}
if (!config.m_initPacketMagicHeader.isEmpty()) {
out << "h1=" << config.m_initPacketMagicHeader << "\n"; out << "h1=" << config.m_initPacketMagicHeader << "\n";
}
if (!config.m_responsePacketMagicHeader.isEmpty()) {
out << "h2=" << config.m_responsePacketMagicHeader << "\n"; out << "h2=" << config.m_responsePacketMagicHeader << "\n";
}
if (!config.m_underloadPacketMagicHeader.isEmpty()) {
out << "h3=" << config.m_underloadPacketMagicHeader << "\n"; out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
}
if (!config.m_transportPacketMagicHeader.isEmpty()) {
out << "h4=" << config.m_transportPacketMagicHeader << "\n"; out << "h4=" << config.m_transportPacketMagicHeader << "\n";
} }
@@ -136,25 +119,24 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
if (err != 0) { if (err != 0) {
logger.error() << "Interface configuration failed:" << strerror(err); logger.error() << "Interface configuration failed:" << strerror(err);
} else { } else {
if (config.m_killSwitchEnabled) { FirewallParams params { };
FirewallParams params { }; params.dnsServers.append(config.m_dnsServer);
params.dnsServers.append(config.m_dnsServer); if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){ params.blockAll = true;
params.blockAll = true; if (config.m_excludedAddresses.size()) {
if (config.m_excludedAddresses.size()) { params.allowNets = true;
params.allowNets = true; foreach (auto net, config.m_excludedAddresses) {
foreach (auto net, config.m_excludedAddresses) { params.allowAddrs.append(net.toUtf8());
params.allowAddrs.append(net.toUtf8());
}
}
} else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString());
} }
} }
applyFirewallRules(params); } else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString());
}
} }
applyFirewallRules(params);
} }
return (err == 0); return (err == 0);
@@ -200,9 +182,7 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
QTextStream out(&message); QTextStream out(&message);
out << "set=1\n"; out << "set=1\n";
out << "public_key=" << QString(publicKey.toHex()) << "\n"; out << "public_key=" << QString(publicKey.toHex()) << "\n";
if (!config.m_serverPskKey.isNull()) { out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
if (!config.m_serverIpv4AddrIn.isNull()) { if (!config.m_serverIpv4AddrIn.isNull()) {
out << "endpoint=" << config.m_serverIpv4AddrIn << ":"; out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
} else if (!config.m_serverIpv6AddrIn.isNull()) { } else if (!config.m_serverIpv6AddrIn.isNull()) {
+5
View File
@@ -22,6 +22,11 @@ class LinuxNetworkWatcher final : public NetworkWatcherImpl {
void start() override; void start() override;
NetworkWatcherImpl::TransportType getTransportType() {
// TODO: Find out how to do that on linux generally. (VPN-2382)
return NetworkWatcherImpl::TransportType_Unknown;
};
signals: signals:
void checkDevicesInThread(); void checkDevicesInThread();
@@ -95,11 +95,6 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
return; return;
} }
// Ignore interface-scoped routes, we want to find the default route to the
// internet in the global scope.
if (rtm->rtm_flags & RTF_IFSCOPE) {
return;
}
// Check for a default route, which should have a netmask of zero. // Check for a default route, which should have a netmask of zero.
const struct sockaddr* sa = const struct sockaddr* sa =
@@ -161,11 +156,6 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
return; return;
} }
// Ignore interface-scoped routes, we want to find the default route to the
// internet in the global scope.
if (rtm->rtm_flags & RTF_IFSCOPE) {
return;
}
// Ignore route changes that we caused, or routes on the tunnel interface. // Ignore route changes that we caused, or routes on the tunnel interface.
if (rtm->rtm_index == m_ifindex) { if (rtm->rtm_index == m_ifindex) {
return; return;

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