Merge branch 'dev' into android-7

# Conflicts:
#	.github/workflows/deploy.yml
#	CMakeLists.txt
#	client/android/src/org/amnezia/vpn/AmneziaActivity.kt
#	client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
#	client/android/src/org/amnezia/vpn/ServiceNotification.kt
#	client/android/utils/src/main/kotlin/Log.kt
#	client/android/utils/src/main/kotlin/net/NetworkState.kt
This commit is contained in:
albexk 2024-10-22 12:51:10 +03:00
commit 5e80223e7a
40 changed files with 252 additions and 112 deletions

View file

@ -16,7 +16,10 @@ jobs:
QT_VERSION: 6.6.2
QIF_VERSION: 4.7
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Install Qt'
@ -83,7 +86,10 @@ jobs:
QIF_VERSION: 4.7
BUILD_ARCH: 64
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Get sources'
@ -146,7 +152,10 @@ jobs:
CC: cc
CXX: c++
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Setup xcode'
@ -238,7 +247,10 @@ jobs:
QT_VERSION: 6.4.3
QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Setup xcode'
@ -304,7 +316,10 @@ jobs:
QT_VERSION: 6.6.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Install desktop Qt'

View file

@ -16,7 +16,10 @@ jobs:
QT_VERSION: 6.4.1
QIF_VERSION: 4.5
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps:
- name: 'Install desktop Qt'

View file

@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 1066)
set(APP_ANDROID_VERSION_CODE 1067)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View file

@ -26,9 +26,11 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)

View file

@ -111,10 +111,11 @@ void AmneziaApplication::init()
qFatal("Android controller initialization failed");
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
m_pageController->goToPageHome();
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
data.clear();
emit m_pageController->goToPageViewConfig();
});
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
@ -122,16 +123,16 @@ void AmneziaApplication::init()
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
m_pageController->goToPageHome();
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
emit m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
m_pageController->goToPageHome();
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
emit m_pageController->goToPageHome();
m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath);
emit m_settingsController->importBackupFromOutside(filePath);
});
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });

View file

@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtMinSdkVersion=26
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64

View file

@ -1,6 +1,5 @@
package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context
import android.net.IpPrefix
import android.net.VpnService
@ -8,9 +7,6 @@ import android.net.VpnService.Builder
import android.os.Build
import android.system.OsConstants
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork

View file

@ -19,6 +19,7 @@ import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.provider.Settings
import android.view.MotionEvent
import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap
import android.widget.Toast
@ -155,7 +156,7 @@ class AmneziaActivity : QtActivity() {
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity: $intent")
Log.d(TAG, "Create Amnezia activity")
loadLibs()
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
@ -190,7 +191,7 @@ class AmneziaActivity : QtActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent")
Log.v(TAG, "onNewIntent: $intent")
intent?.let(::processIntent)
}
@ -377,7 +378,7 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun startVpn(vpnConfig: String) {
getVpnProto(vpnConfig)?.let { proto ->
Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
Log.v(TAG, "Proto from config: $proto, current proto: $vpnProto")
if (isServiceConnected) {
if (proto.serviceClass == vpnProto?.serviceClass) {
vpnProto = proto
@ -490,7 +491,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = {
it?.data?.let { uri ->
Log.d(TAG, "Save file to $uri")
Log.v(TAG, "Save file to $uri")
try {
contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) }
@ -539,7 +540,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
onAny = {
val uri = it?.data?.toString() ?: ""
Log.d(TAG, "Open file: $uri")
Log.v(TAG, "Open file: $uri")
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
@ -694,6 +695,66 @@ class AmneziaActivity : QtActivity() {
}
}
// workaround for a bug in Qt that causes the mouse click event not to be handled
// also disable right-click, as it causes the application to crash
private var lastButtonState = 0
private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
pointerCount,
(0 until pointerCount).map { i ->
MotionEvent.PointerProperties().apply {
getPointerProperties(i, this)
}
}.toTypedArray(),
(0 until pointerCount).map { i ->
MotionEvent.PointerCoords().apply {
getPointerCoords(i, this)
}
}.toTypedArray(),
metaState,
MotionEvent.BUTTON_PRIMARY,
xPrecision,
yPrecision,
deviceId,
edgeFlags,
source,
flags
)
private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
lastButtonState = ev.buttonState
if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
}
MotionEvent.ACTION_UP -> {
when (lastButtonState) {
MotionEvent.BUTTON_SECONDARY -> return true
MotionEvent.BUTTON_PRIMARY -> {
val modEvent = ev.fixCopy()
return superDispatch(modEvent).apply { modEvent.recycle() }
}
}
}
}
return superDispatch(ev)
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
}
return super.dispatchTouchEvent(ev)
}
override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
return super.dispatchTrackballEvent(ev)
}
/**
* Utils methods
*/

View file

@ -285,7 +285,7 @@ open class AmneziaVpnService : VpnService() {
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) {
it?.action?.let { action ->
Log.d(TAG, "Broadcast request received: $action")
Log.v(TAG, "Broadcast request received: $action")
when (action) {
ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect()
@ -416,7 +416,7 @@ open class AmneziaVpnService : VpnService() {
serviceNotification.isNotificationEnabled() &&
getSystemService<PowerManager>()?.isInteractive != false
) {
Log.d(TAG, "Launch traffic stats update")
Log.v(TAG, "Launch traffic stats update")
trafficStats.reset()
startTrafficStatsUpdateJob()
}

View file

@ -66,7 +66,7 @@ class AuthActivity : FragmentActivity() {
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d(TAG, "Authentication succeeded")
Log.v(TAG, "Authentication succeeded")
QtAndroidController.onAuthResult(true)
finish()
}

View file

@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Create Import Config Activity: $intent")
Log.v(TAG, "Create Import Config Activity: $intent")
intent?.let(::readConfig)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent")
Log.v(TAG, "onNewIntent: $intent")
intent.let(::readConfig)
}
private fun readConfig(intent: Intent) {
when (intent.action) {
ACTION_SEND -> {
Log.d(TAG, "Process SEND action, type: ${intent.type}")
Log.v(TAG, "Process SEND action, type: ${intent.type}")
when (intent.type) {
"application/octet-stream" -> {
intent.getUriCompat()?.let { uri ->
@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
}
ACTION_VIEW -> {
Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
when (intent.scheme) {
"file", "content" -> {
intent.data?.let { uri ->

View file

@ -58,7 +58,7 @@ class ServiceNotification(private val context: Context) {
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null
Log.d(TAG, "Build notification: $serverName, $state")
Log.v(TAG, "Build notification: $serverName, $state")
return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round)
@ -85,7 +85,7 @@ class ServiceNotification(private val context: Context) {
@SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
Log.d(TAG, "Update notification: $serverName, $state")
Log.v(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
}

View file

@ -46,7 +46,7 @@ object LibraryLoader {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.d(TAG, "Failed to load library, try to extract it from apk")
Log.w(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {

View file

@ -40,11 +40,11 @@ class NetworkState(
private val networkCallback: NetworkCallback by lazy(NONE) {
object : NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d(TAG, "onAvailable: $network")
Log.v(TAG, "onAvailable: $network")
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
handler.post {
checkNetworkState(network, networkCapabilities)
}
@ -67,11 +67,11 @@ class NetworkState(
}
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
Log.d(TAG, "onBlockedStatusChanged: $network, $blocked")
Log.v(TAG, "onBlockedStatusChanged: $network, $blocked")
}
override fun onLost(network: Network) {
Log.d(TAG, "onLost: $network")
Log.v(TAG, "onLost: $network")
}
}
}

View file

@ -66,7 +66,7 @@ open class Wireguard : Protocol() {
try {
delay(1000)
var log = getLogcat(time)
Log.d(TAG, "First waiting log: $log")
Log.v(TAG, "First waiting log: $log")
// check that there is a connection log,
// to avoid infinite connection
if (!log.contains("Attaching to interface")) {

View file

@ -130,8 +130,8 @@ class Xray : Protocol() {
LibXray.initXray(assetsPath)
val geoDir = File(assetsPath, "geo").absolutePath
val configPath = File(context.cacheDir, "config.json")
Log.d(TAG, "xray.location.asset: $geoDir")
Log.d(TAG, "config: $configPath")
Log.v(TAG, "xray.location.asset: $geoDir")
Log.v(TAG, "config: $configPath")
try {
configPath.writeText(configJson)
} catch (e: IOException) {

View file

@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 24)
set(APP_ANDROID_MIN_SDK 26)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)

View file

@ -12,6 +12,7 @@
#include "configurators/wireguard_configurator.h"
#include "core/enums/apiEnums.h"
#include "version.h"
#include "utilities.h"
namespace
{
@ -40,9 +41,10 @@ namespace
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
const QStringList proxyStorageUrl = { "" };
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{
@ -94,8 +96,8 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = serverConfig.value(config_key::containers).toArray();
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = newServerConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
return; // todo process error
}
@ -114,25 +116,30 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig;
containers.replace(0, container);
serverConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(serverConfig).toJson());
newServerConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(newServerConfig).toJson());
}
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[config_key::containers] = apiConfig.value(config_key::containers);
serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName);
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion);
serverConfig[config_key::description] = apiConfig.value(config_key::description);
serverConfig[config_key::name] = apiConfig.value(config_key::name);
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
}
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer;
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
auto apiConfig = QJsonObject::fromVariantMap(map);
serverConfig[configKey::apiConfig] = apiConfig;
return;
}
@ -146,6 +153,15 @@ QStringList ApiController::getProxyUrls()
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList proxyStorageUrl;
if (m_isDevEnvironment) {
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
} else {
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl);
reply = amnApp->manager()->get(request);
@ -166,11 +182,23 @@ QStringList ApiController::getProxyUrls()
EVP_PKEY *privateKey = nullptr;
QByteArray responseBody;
try {
QByteArray key = PROD_PROXY_STORAGE_KEY;
QSimpleCrypto::QRsa rsa;
privateKey = rsa.getPrivateKeyFromByteArray(key, "");
responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING);
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
QByteArray key = QByteArray::fromHex(hashResult.left(64));
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
QSimpleCrypto::QBlockCipher blockCipher;
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
} else {
responseBody = encryptedResponseBody;
}
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload";
return {};
}
@ -316,7 +344,8 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
}
ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig)
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,
QJsonObject &serverConfig)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
@ -339,6 +368,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
}
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid;
apiPayload[configKey::authData] = authData;
QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32);
@ -361,6 +391,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
} catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables";
return ErrorCode::ApiMissingAgwPublicKey;
}
@ -370,7 +401,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when encrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
QJsonObject requestBody;
@ -416,7 +449,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
return errorCode;

View file

@ -21,7 +21,7 @@ public slots:
ErrorCode getServicesList(QByteArray &responseBody);
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig);
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig);
signals:
void errorOccurred(ErrorCode errorCode);

View file

@ -96,6 +96,7 @@ namespace amnezia
// import and install errors
ImportInvalidConfigError = 900,
ImportOpenConfigError = 901,
// Android errors
AndroidError = 1000,
@ -107,6 +108,7 @@ namespace amnezia
ApiConfigTimeoutError = 1103,
ApiConfigSslError = 1104,
ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
// QFile errors
OpenError = 1200,

View file

@ -50,6 +50,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break;
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break;
// Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
@ -61,6 +62,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break;
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View file

@ -78,7 +78,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
return false;
}
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
if (!dnsutils()->restoreResolvers()) {
return false;
}
@ -165,10 +165,6 @@ bool Daemon::activate(const InterfaceConfig& config) {
}
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
if (!supportDnsUtils()) {
return true;
}
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
(config.m_hopType == InterfaceConfig::SingleHop)) {
QList<QHostAddress> resolvers;
@ -423,13 +419,8 @@ bool Daemon::deactivate(bool emitSignals) {
}
// Cleanup DNS
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
return false;
}
if (!wgutils()->interfaceExists()) {
logger.warning() << "Wireguard interface does not exist.";
return false;
if (!dnsutils()->restoreResolvers()) {
logger.warning() << "Failed to restore DNS resolvers.";
}
// Cleanup peers and routing
@ -449,13 +440,9 @@ bool Daemon::deactivate(bool emitSignals) {
}
m_excludedAddrSet.clear();
// Delete the interface
if (!wgutils()->deleteInterface()) {
return false;
}
m_connections.clear();
return true;
// Delete the interface
return wgutils()->deleteInterface();
}
QString Daemon::logs() {

View file

@ -69,7 +69,6 @@ class Daemon : public QObject {
virtual WireguardUtils* wgutils() const = 0;
virtual bool supportIPUtils() const { return false; }
virtual IPUtils* iputils() { return nullptr; }
virtual bool supportDnsUtils() const { return false; }
virtual DnsUtils* dnsutils() { return nullptr; }
static bool parseStringList(const QJsonObject& obj, const QString& name,

View file

@ -92,6 +92,17 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.debug() << "Command received:" << type;
// It is expected that sometimes the client will request backend logs
// before the first authentication. In these cases we just return empty
// logs.
if (type == "logs") {
QJsonObject obj;
obj.insert("type", "logs");
obj.insert("logs", "");
write(obj);
return;
}
if (type == "activate") {
InterfaceConfig config;
if (!Daemon::parseConfig(obj, config)) {
@ -115,8 +126,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
if (type == "status") {
QJsonObject obj = Daemon::instance()->getStatus();
obj.insert("type", "status");
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_socket->write("\n");
write(obj);
return;
}
@ -124,8 +134,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
QJsonObject obj;
obj.insert("type", "logs");
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_socket->write("\n");
write(obj);
return;
}

View file

@ -35,7 +35,7 @@ LocalSocketController::LocalSocketController() {
connect(m_socket, &QLocalSocket::connected, this,
&LocalSocketController::daemonConnected);
connect(m_socket, &QLocalSocket::disconnected, this,
&LocalSocketController::disconnected);
[&] { errorOccurred(QLocalSocket::PeerClosedError); });
connect(m_socket, &QLocalSocket::errorOccurred, this,
&LocalSocketController::errorOccurred);
connect(m_socket, &QLocalSocket::readyRead, this,

View file

@ -22,7 +22,6 @@ class LinuxDaemon final : public Daemon {
protected:
WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; }

View file

@ -21,7 +21,6 @@ class MacOSDaemon final : public Daemon {
protected:
WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; }

View file

@ -26,7 +26,6 @@ class WindowsDaemon final : public Daemon {
protected:
bool run(Op op, const InterfaceConfig& config) override;
WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; }
private:

View file

@ -502,7 +502,7 @@ QString WindowsSplitTunnel::convertPath(const QString& path) {
// device should contain : for e.g C:
return "";
}
QByteArray buffer(2048, 0xFF);
QByteArray buffer(2048, 0xFFu);
auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter),
(wchar_t*)buffer.data(), buffer.size() / 2);

View file

@ -21,7 +21,7 @@
#include "platforms/windows/windowsutils.h"
constexpr const char* VPN_NAME = "AmneziaVPN";
constexpr const char* WIREGUARD_DIR = "WireGuard";
constexpr const char* WIREGUARD_DIR = "AmneziaWG";
constexpr const char* DATA_DIR = "Data";
namespace {

View file

@ -51,6 +51,9 @@ void ConnectionController::openConnection()
if (configVersion == ApiConfigSources::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromTelegram();
} else if (configVersion == ApiConfigSources::AmneziaGateway
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromGateway();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by end_date event";
if (configVersion == ApiConfigSources::Telegram) {

View file

@ -39,11 +39,12 @@ namespace
const QString amneziaConfigPatternUserName = "userName";
const QString amneziaConfigPatternPassword = "password";
const QString amneziaFreeConfigPattern = "api_key";
const QString amneziaPremiumConfigPattern = "auth_data";
const QString backupPattern = "Servers/serversList";
if (config.contains(backupPattern)) {
return ConfigTypes::Backup;
} else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern)
} else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern) || config.contains(amneziaPremiumConfigPattern)
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia;
@ -84,7 +85,7 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
return extractConfigFromData(data);
}
emit importErrorOccurred(tr("Unable to open file"), false);
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
return false;
}
@ -188,12 +189,12 @@ bool ImportController::extractConfigFromData(QString data)
if (!m_serversModel->getServersCount()) {
emit restoreAppConfig(config.toUtf8());
} else {
emit importErrorOccurred(tr("Invalid configuration file"), false);
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
}
break;
}
case ConfigTypes::Invalid: {
emit importErrorOccurred(tr("Invalid configuration file"), false);
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
break;
}
}

View file

@ -54,7 +54,6 @@ public slots:
signals:
void importFinished();
void importErrorOccurred(const QString &errorMessage, bool goToPageHome);
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void qrDecodingFinished();

View file

@ -32,6 +32,7 @@ namespace
constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
#ifdef Q_OS_WINDOWS
@ -135,10 +136,10 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
} else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
containerConfig.insert(config_key::password, Utils::getRandomString(16));
} else if (container == DockerContainer::Socks5Proxy) {
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
containerConfig.insert(config_key::password, Utils::getRandomString(16));
}
config.insert(config_key::container, ContainerProps::containerToString(container));
@ -768,7 +769,7 @@ bool InstallController::checkSshConnection(QSharedPointer<ServerController> serv
} else {
if (output.contains(tr("Please login as the user"))) {
output.replace("\n", "");
emit installationErrorOccurred(output);
emit wrongInstallationUser(output);
return false;
}
}
@ -826,7 +827,7 @@ bool InstallController::installServiceFromApi()
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig);
m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
@ -853,19 +854,19 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
QJsonObject newServerConfig;
ErrorCode errorCode =
apiController.getConfigForService(m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(),
apiConfig.value(configKey::serviceType).toString(),
apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, newServerConfig);
ErrorCode errorCode = apiController.getConfigForService(
m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(),
apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode,
authData, newServerConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
newApiConfig.insert(configKey::serviceInfo, apiConfig.value(configKey::serviceInfo));
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));

View file

@ -75,8 +75,8 @@ signals:
void removeAllContainersFinished(const QString &finishedMessage);
void removeProcessedContainerFinished(const QString &finishedMessage);
void installationErrorOccurred(const QString &errorMessage);
void installationErrorOccurred(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverAlreadyExists(int serverIndex);

View file

@ -77,6 +77,7 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
{
beginResetModel();
m_clientsTable = QJsonArray();
endResetModel();
ErrorCode error = ErrorCode::NoError;
@ -90,10 +91,10 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server";
endResetModel();
return error;
}
beginResetModel();
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) {
@ -601,5 +602,6 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
roles[LatestHandshakeRole] = "latestHandshake";
roles[DataReceivedRole] = "dataReceived";
roles[DataSentRole] = "dataSent";
roles[AllowedIpsRole] = "allowedIps";
return roles;
}

View file

@ -37,7 +37,7 @@ PageType {
Connections {
target: ImportController
function onImportErrorOccurred(errorMessage, goToPageHome) {
function onImportErrorOccurred(error, goToPageHome) {
if (goToPageHome) {
PageController.goToStartPage()
} else {

View file

@ -123,6 +123,10 @@ PageType {
}
}
function onWrongInstallationUser(message) {
onInstallationErrorOccurred(message)
}
function onUpdateContainerFinished(message) {
PageController.showNotificationMessage(message)
PageController.closePage()

View file

@ -14,14 +14,13 @@
QString Utils::getRandomString(int len)
{
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
const QString possibleCharacters = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString;
for (int i = 0; i < len; ++i) {
quint32 index = QRandomGenerator::global()->generate() % possibleCharacters.length();
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
randomString.append(possibleCharacters.at(QRandomGenerator::system()->bounded(possibleCharacters.length())));
}
return randomString;
}
@ -244,3 +243,22 @@ bool Utils::signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
}
#endif
void Utils::logException(const std::exception &e)
{
qCritical() << e.what();
try {
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
logException(nested);
} catch (...) {}
}
void Utils::logException(const std::exception_ptr &eptr)
{
try {
if (eptr) std::rethrow_exception(eptr);
} catch (const std::exception &e) {
logException(e);
} catch (...) {}
}

View file

@ -34,6 +34,9 @@ public:
static QString certUtilPath();
static QString tun2socksPath();
static void logException(const std::exception &e);
static void logException(const std::exception_ptr &eptr = std::current_exception());
#ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
#endif