Compare commits

..

2 Commits

Author SHA1 Message Date
Pokamest Nikak 2c492f4033 wip 2024-09-19 14:28:06 +01:00
pokamest 471958479e WIP 2024-09-19 06:24:43 -07:00
96 changed files with 1689 additions and 1064 deletions
+2 -17
View File
@@ -16,10 +16,7 @@ jobs:
QT_VERSION: 6.6.2 QT_VERSION: 6.6.2
QIF_VERSION: 4.7 QIF_VERSION: 4.7
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install Qt' - name: 'Install Qt'
@@ -86,10 +83,7 @@ jobs:
QIF_VERSION: 4.7 QIF_VERSION: 4.7
BUILD_ARCH: 64 BUILD_ARCH: 64
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Get sources' - name: 'Get sources'
@@ -152,10 +146,7 @@ jobs:
CC: cc CC: cc
CXX: c++ CXX: c++
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@@ -190,7 +181,7 @@ jobs:
- name: 'Install go' - name: 'Install go'
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.22.1' go-version: '1.20'
cache: false cache: false
- name: 'Setup gomobile' - name: 'Setup gomobile'
@@ -247,10 +238,7 @@ jobs:
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
QIF_VERSION: 4.6 QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@@ -313,13 +301,10 @@ jobs:
env: env:
ANDROID_BUILD_PLATFORM: android-34 ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.7.3 QT_VERSION: 6.7.2
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
-3
View File
@@ -16,10 +16,7 @@ jobs:
QT_VERSION: 6.4.1 QT_VERSION: 6.4.1
QIF_VERSION: 4.5 QIF_VERSION: 4.5
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
+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.8.2.3 project(${PROJECT} VERSION 4.8.0.5
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 2069) set(APP_ANDROID_VERSION_CODE 61)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+8 -4
View File
@@ -10,17 +10,21 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
<br> <br>
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download.png" width="150" style="max-width: 100%;"></a> <a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_Linux_4.7.0.0.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.7.0.0"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
<br>
<a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/play.png" width="150" style="max-width: 100%;"></a> <a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/play.png" width="150" style="max-width: 100%;"></a>
<a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/apl.png" width="150" style="max-width: 100%;"></a> <a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/apl.png" width="150" style="max-width: 100%;"></a>
[Alternative download link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org/downloads)
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases) [All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
<br> <br>
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
## Features ## Features
@@ -33,7 +37,7 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
## Links ## Links
- [https://amnezia.org](https://amnezia.org) - project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org) - [https://amnezia.org](https://amnezia.org) - project website
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English) - [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi) - [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
@@ -0,0 +1,25 @@
include_directories(${CMAKE_CURRENT_LIST_DIR})
find_package(Qt6 REQUIRED COMPONENTS
Core Network
)
set(LIBS ${LIBS} Qt6::Core Qt6::Network)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/singleapplication.h
${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/singleapplication.cpp
${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.cpp
)
if(WIN32)
if(MSVC)
set(LIBS ${LIBS} Advapi32.lib)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(LIBS ${LIBS} advapi32)
endif()
endif()
@@ -0,0 +1,274 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <QtCore/QElapsedTimer>
#include <QtCore/QByteArray>
#include <QtCore/QSharedMemory>
#include "singleapplication.h"
#include "singleapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
* @param argc
* @param argv
* @param allowSecondary Whether to enable secondary instance support
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
{
Q_D( SingleApplication );
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
// On Android and iOS since the library is not supported fallback to
// standard QApplication behaviour by simply returning at this point.
qWarning() << "SingleApplication is not supported on Android and iOS systems.";
return;
#endif
// Store the current mode of the program
d->options = options;
// Add any unique user data
if ( ! userData.isEmpty() )
d->addAppData( userData );
// Generating an application ID used for identifying the shared memory
// block and QLocalServer
d->genBlockServerName();
// To mitigate QSharedMemory issues with large amount of processes
// attempting to attach at the same time
SingleApplicationPrivate::randomSleep();
#ifdef Q_OS_UNIX
// By explicitly attaching it and then deleting it we make sure that the
// memory is deleted even after the process has crashed on Unix.
d->memory = new QSharedMemory( d->blockServerName );
d->memory->attach();
delete d->memory;
#endif
// Guarantee thread safe behaviour with a shared memory block.
d->memory = new QSharedMemory( d->blockServerName );
// Create a shared memory block
if( d->memory->create( sizeof( InstancesInfo ) )){
// Initialize the shared memory block
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after create.";
abortSafely();
}
d->initializeMemoryBlock();
} else {
if( d->memory->error() == QSharedMemory::AlreadyExists ){
// Attempt to attach to the memory segment
if( ! d->memory->attach() ){
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
abortSafely();
}
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
abortSafely();
}
} else {
qCritical() << "SingleApplication: Unable to create block.";
abortSafely();
}
}
auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state
while( true ){
// If the shared memory block's checksum is valid continue
if( d->blockChecksum() == inst->checksum ) break;
// If more than 5s have elapsed, assume the primary instance crashed and
// assume it's position
if( time.elapsed() > 5000 ){
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
d->initializeMemoryBlock();
}
// Otherwise wait for a random period and try again. The random sleep here
// limits the probability of a collision between two racing apps and
// allows the app to initialise faster
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
qDebug() << d->memory->errorString();
}
SingleApplicationPrivate::randomSleep();
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
abortSafely();
}
}
if( inst->primary == false ){
d->startPrimary();
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
qDebug() << d->memory->errorString();
}
return;
}
// Check if another instance can be started
if( allowSecondary ){
d->startSecondary();
if( d->options & Mode::SecondaryNotification ){
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
qDebug() << d->memory->errorString();
}
return;
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
qDebug() << d->memory->errorString();
}
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
delete d;
::exit( EXIT_SUCCESS );
}
SingleApplication::~SingleApplication()
{
Q_D( SingleApplication );
delete d;
}
/**
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
bool SingleApplication::isPrimary() const
{
Q_D( const SingleApplication );
return d->server != nullptr;
}
/**
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
bool SingleApplication::isSecondary() const
{
Q_D( const SingleApplication );
return d->server == nullptr;
}
/**
* Allows you to identify an instance by returning unique consecutive instance
* ids. It is reset when the first (primary) instance of your app starts and
* only incremented afterwards.
* @return Returns a unique instance id.
*/
quint32 SingleApplication::instanceId() const
{
Q_D( const SingleApplication );
return d->instanceNumber;
}
/**
* Returns the OS PID (Process Identifier) of the process running the primary
* instance. Especially useful when SingleApplication is coupled with OS.
* specific APIs.
* @return Returns the primary instance PID.
*/
qint64 SingleApplication::primaryPid() const
{
Q_D( const SingleApplication );
return d->primaryPid();
}
/**
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
QString SingleApplication::primaryUser() const
{
Q_D( const SingleApplication );
return d->primaryUser();
}
/**
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
QString SingleApplication::currentUser() const
{
return SingleApplicationPrivate::getUsername();
}
/**
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfuly, false otherwise.
*/
bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
{
Q_D( SingleApplication );
// Nobody to connect to
if( isPrimary() ) return false;
// Make sure the socket is connected
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false;
d->socket->write( message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
}
/**
* Cleans up the shared memory block and exits with a failure.
* This function halts program execution.
*/
void SingleApplication::abortSafely()
{
Q_D( SingleApplication );
qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
delete d;
::exit( EXIT_FAILURE );
}
QStringList SingleApplication::userData() const
{
Q_D( const SingleApplication );
return d->appData();
}
@@ -0,0 +1,154 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H
#include <QtCore/QtGlobal>
#include <QtNetwork/QLocalSocket>
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QApplication
#endif
#include QT_STRINGIFY(QAPPLICATION_CLASS)
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multiple instances of the same
* Application
* @see QCoreApplication
*/
class SingleApplication : public QAPPLICATION_CLASS
{
Q_OBJECT
using app_t = QAPPLICATION_CLASS;
public:
/**
* @brief Mode of operation of SingleApplication.
* Whether the block should be user-wide or system-wide and whether the
* primary instance should be notified when a secondary instance had been
* started.
* @note Operating system can restrict the shared memory blocks to the same
* user, in which case the User/System modes will have no effect and the
* block will be user wide.
* @enum
*/
enum Mode {
User = 1 << 0,
System = 1 << 1,
SecondaryNotification = 1 << 2,
ExcludeAppVersion = 1 << 3,
ExcludeAppPath = 1 << 4
};
Q_DECLARE_FLAGS(Options, Mode)
/**
* @brief Intitializes a SingleApplication instance with argc command line
* arguments in argv
* @arg {int &} argc - Number of arguments in argv
* @arg {const char *[]} argv - Supplied command line arguments
* @arg {bool} allowSecondary - Whether to start the instance as secondary
* if there is already a primary instance.
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
* User wide or System wide.
* @arg {int} timeout - Timeout to wait in milliseconds.
* @note argc and argv may be changed as Qt removes arguments that it
* recognizes
* @note Mode::SecondaryNotification only works if set on both the primary
* instance and the secondary instance.
* @note The timeout is just a hint for the maximum time of blocking
* operations. It does not guarantee that the SingleApplication
* initialisation will be completed in given time, though is a good hint.
* Usually 4*timeout would be the worst case (fail) scenario.
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
*/
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
~SingleApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting
* @returns {bool}
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage( const QByteArray &message, int timeout = 100 );
/**
* @brief Get the set user data.
* @returns {QStringList}
*/
QStringList userData() const;
Q_SIGNALS:
void instanceStarted();
void receivedMessage( quint32 instanceId, QByteArray message );
private:
SingleApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleApplication)
void abortSafely();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
#endif // SINGLE_APPLICATION_H
@@ -0,0 +1,15 @@
QT += core network
CONFIG += c++11
HEADERS += \
$$PWD/singleapplication.h \
$$PWD/singleapplication_p.h
SOURCES += $$PWD/singleapplication.cpp \
$$PWD/singleapplication_p.cpp
INCLUDEPATH += $$PWD
win32 {
msvc:LIBS += Advapi32.lib
gcc:LIBS += -ladvapi32
}
@@ -0,0 +1,486 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <cstdlib>
#include <cstddef>
#include <QtCore/QDir>
#include <QtCore/QThread>
#include <QtCore/QByteArray>
#include <QtCore/QDataStream>
#include <QtCore/QElapsedTimer>
#include <QtCore/QCryptographicHash>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
#include <QtCore/QRandomGenerator>
#else
#include <QtCore/QDateTime>
#endif
#include "singleapplication.h"
#include "singleapplication_p.h"
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif
#ifdef Q_OS_WIN
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#include <lmcons.h>
#endif
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
: q_ptr( q_ptr )
{
server = nullptr;
socket = nullptr;
memory = nullptr;
instanceNumber = 0;
}
SingleApplicationPrivate::~SingleApplicationPrivate()
{
if( socket != nullptr ){
socket->close();
delete socket;
}
if( memory != nullptr ){
memory->lock();
auto *inst = static_cast<InstancesInfo*>(memory->data());
if( server != nullptr ){
server->close();
delete server;
inst->primary = false;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
memory->unlock();
delete memory;
}
}
QString SingleApplicationPrivate::getUsername()
{
#ifdef Q_OS_WIN
wchar_t username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = UNLEN + 1;
if( GetUserNameW( username, &usernameLength ) )
return QString::fromWCharArray( username );
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
#else
return qEnvironmentVariable( "USERNAME" );
#endif
#endif
#ifdef Q_OS_UNIX
QString username;
uid_t uid = geteuid();
struct passwd *pw = getpwuid( uid );
if( pw )
username = QString::fromLocal8Bit( pw->pw_name );
if ( username.isEmpty() ){
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
username = QString::fromLocal8Bit( qgetenv( "USER" ) );
#else
username = qEnvironmentVariable( "USER" );
#endif
}
return username;
#endif
}
void SingleApplicationPrivate::genBlockServerName()
{
QCryptographicHash appData( QCryptographicHash::Sha256 );
appData.addData( "SingleApplication", 17 );
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
if ( ! appDataList.isEmpty() )
appData.addData( appDataList.join( "" ).toUtf8() );
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
}
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
#ifdef Q_OS_WIN
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
#else
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
#endif
}
// User level block requires a user specific data in the hash
if( options & SingleApplication::Mode::User ){
appData.addData( getUsername().toUtf8() );
}
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
// server naming requirements.
blockServerName = appData.result().toBase64().replace("/", "_");
}
void SingleApplicationPrivate::initializeMemoryBlock() const
{
auto *inst = static_cast<InstancesInfo*>( memory->data() );
inst->primary = false;
inst->secondary = 0;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
void SingleApplicationPrivate::startPrimary()
{
// Reset the number of connections
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->primary = true;
inst->primaryPid = QCoreApplication::applicationPid();
qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
inst->checksum = blockChecksum();
instanceNumber = 0;
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
// Restrict access to the socket according to the
// SingleApplication::Mode::User flag on User level or no restrictions
if( options & SingleApplication::Mode::User ){
server->setSocketOptions( QLocalServer::UserAccessOption );
} else {
server->setSocketOptions( QLocalServer::WorldAccessOption );
}
server->listen( blockServerName );
QObject::connect(
server,
&QLocalServer::newConnection,
this,
&SingleApplicationPrivate::slotConnectionEstablished
);
}
void SingleApplicationPrivate::startSecondary()
{
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->secondary += 1;
inst->checksum = blockChecksum();
instanceNumber = inst->secondary;
}
bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
{
QElapsedTimer time;
time.start();
// Connect to the Local Server of the Primary Instance if not already
// connected.
if( socket == nullptr ){
socket = new QLocalSocket();
}
if( socket->state() == QLocalSocket::ConnectedState ) return true;
if( socket->state() != QLocalSocket::ConnectedState ){
while( true ){
randomSleep();
if( socket->state() != QLocalSocket::ConnectingState )
socket->connectToServer( blockServerName );
if( socket->state() == QLocalSocket::ConnectingState ){
socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
}
// If connected break out of the loop
if( socket->state() == QLocalSocket::ConnectedState ) break;
// If elapsed time since start is longer than the method timeout return
if( time.elapsed() >= msecs ) return false;
}
}
// Initialisation message according to the SingleApplication protocol
QByteArray initMsg;
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
writeStream.setVersion(QDataStream::Qt_5_6);
#endif
writeStream << blockServerName.toLatin1();
writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif
writeStream << checksum;
// The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
headerStream << static_cast <quint64>( initMsg.length() );
socket->write( header );
socket->write( initMsg );
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
socket->flush();
return result;
}
quint16 SingleApplicationPrivate::blockChecksum() const
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
#else
quint16 checksum = qChecksum(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum));
#endif
return checksum;
}
qint64 SingleApplicationPrivate::primaryPid() const
{
qint64 pid;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
pid = inst->primaryPid;
memory->unlock();
return pid;
}
QString SingleApplicationPrivate::primaryUser() const
{
QByteArray username;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
username = inst->primaryUser;
memory->unlock();
return QString::fromUtf8( username );
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished()
{
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
}
);
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
[nextConnSocket, this](){
connectionMap.remove(nextConnSocket);
}
);
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
switch(info.stage){
case StageHeader:
readInitMessageHeader(nextConnSocket);
break;
case StageBody:
readInitMessageBody(nextConnSocket);
break;
case StageConnected:
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
break;
default:
break;
};
}
);
}
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )){
return;
}
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
return;
}
QDataStream headerStream( sock );
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion( QDataStream::Qt_5_6 );
#endif
// Read the header to know the message length
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock];
info.stage = StageBody;
info.msgLen = msgLen;
if ( sock->bytesAvailable() >= (qint64) msgLen ){
readInitMessageBody( sock );
}
}
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{
Q_Q(SingleApplication);
if (!connectionMap.contains( sock )){
return;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return;
}
// Read the message body
QByteArray msgBytes = sock->read(info.msgLen);
QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
readStream.setVersion( QDataStream::Qt_5_6 );
#endif
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast <ConnectionType>( connTypeVal );
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif
bool isValid = readStream.status() == QDataStream::Ok &&
QLatin1String(latin1Name) == blockServerName &&
msgChecksum == actualChecksum;
if( !isValid ){
sock->close();
return;
}
info.instanceId = instanceId;
info.stage = StageConnected;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
options & SingleApplication::Mode::SecondaryNotification ) )
{
Q_EMIT q->instanceStarted();
}
if (sock->bytesAvailable() > 0){
Q_EMIT this->slotDataAvailable( sock, instanceId );
}
}
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{
Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
}
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId );
}
void SingleApplicationPrivate::randomSleep()
{
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
#endif
}
void SingleApplicationPrivate::addAppData(const QString &data)
{
appDataList.push_back(data);
}
QStringList SingleApplicationPrivate::appData() const
{
return appDataList;
}
@@ -0,0 +1,104 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#ifndef SINGLEAPPLICATION_P_H
#define SINGLEAPPLICATION_P_H
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include "singleapplication.h"
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
char primaryUser[128];
quint16 checksum; // Must be the last field
};
struct ConnectionInfo {
qint64 msgLen = 0;
quint32 instanceId = 0;
quint8 stage = 0;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
enum ConnectionType : quint8 {
InvalidConnection = 0,
NewInstance = 1,
SecondaryInstance = 2,
Reconnect = 3
};
enum ConnectionStage : quint8 {
StageHeader = 0,
StageBody = 1,
StageConnected = 2,
};
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr );
~SingleApplicationPrivate() override;
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary( int msecs, ConnectionType connectionType );
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket);
static void randomSleep();
void addAppData(const QString &data);
QStringList appData() const;
SingleApplication *q_ptr;
QSharedMemory *memory;
QLocalSocket *socket;
QLocalServer *server;
quint32 instanceNumber;
QString blockServerName;
SingleApplication::Options options;
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
QStringList appDataList;
public Q_SLOTS:
void slotConnectionEstablished();
void slotDataAvailable( QLocalSocket*, quint32 );
void slotClientConnectionClosed( QLocalSocket*, quint32 );
};
#endif // SINGLEAPPLICATION_P_H
+1 -3
View File
@@ -25,11 +25,10 @@ execute_process(
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}") add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}") add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}") add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}") add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
if(IOS) if(IOS)
set(PACKAGES ${PACKAGES} Multimedia) set(PACKAGES ${PACKAGES} Multimedia)
@@ -62,7 +61,6 @@ qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep) qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep) qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
endif() endif()
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc) qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
+24 -30
View File
@@ -10,8 +10,6 @@
#include <QTextDocument> #include <QTextDocument>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include <QLocalSocket>
#include <QLocalServer>
#include "logger.h" #include "logger.h"
#include "ui/models/installedAppsModel.h" #include "ui/models/installedAppsModel.h"
@@ -30,7 +28,12 @@
#include <AmneziaVPN-Swift.h> #include <AmneziaVPN-Swift.h>
#endif #endif
#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
AmneziaApplication::AmneziaApplication(int &argc, char *argv[])
: QApplication(argc, argv)
#endif
{ {
setQuitOnLastWindowClosed(false); setQuitOnLastWindowClosed(false);
@@ -111,11 +114,10 @@ void AmneziaApplication::init()
qFatal("Android controller initialization failed"); qFatal("Android controller initialization failed");
} }
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) { connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
emit m_pageController->goToPageHome(); m_pageController->goToPageHome();
m_importController->extractConfigFromData(data); m_importController->extractConfigFromData(data);
data.clear(); m_pageController->goToPageViewConfig();
emit m_pageController->goToPageViewConfig();
}); });
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider); m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
@@ -123,16 +125,16 @@ void AmneziaApplication::init()
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->initialize(); IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) { connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
emit m_pageController->goToPageHome(); m_pageController->goToPageHome();
m_importController->extractConfigFromData(data); m_importController->extractConfigFromData(data);
emit m_pageController->goToPageViewConfig(); m_pageController->goToPageViewConfig();
}); });
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) { connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
emit m_pageController->goToPageHome(); m_pageController->goToPageHome();
m_pageController->goToPageSettingsBackup(); m_pageController->goToPageSettingsBackup();
emit m_settingsController->importBackupFromOutside(filePath); m_settingsController->importBackupFromOutside(filePath);
}); });
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); }); QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
@@ -177,6 +179,16 @@ void AmneziaApplication::init()
m_pageController->showOnStartup(); m_pageController->showOnStartup();
#endif #endif
// TODO - fix
// #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
// if (isPrimary()) {
// QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
// qDebug() << "Secondary instance started, showing this window instead";
// emit m_pageController->raiseMainWindow();
// });
// }
// #endif
// Android TextArea clipboard workaround // Android TextArea clipboard workaround
// Text from TextArea always has "text/html" mime-type: // Text from TextArea always has "text/html" mime-type:
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865 // /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
@@ -281,24 +293,6 @@ bool AmneziaApplication::parseCommands()
return true; return true;
} }
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
void AmneziaApplication::startLocalServer() {
const QString serverName("AmneziaVPNInstance");
QLocalServer::removeServer(serverName);
QLocalServer* server = new QLocalServer(this);
server->listen(serverName);
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
if (server) {
QLocalSocket* clientConnection = server->nextPendingConnection();
clientConnection->deleteLater();
}
emit m_pageController->raiseMainWindow();
});
}
#endif
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{ {
return m_engine; return m_engine;
+5 -4
View File
@@ -54,13 +54,18 @@
#define AMNEZIA_BASE_CLASS QGuiApplication #define AMNEZIA_BASE_CLASS QGuiApplication
#else #else
#define AMNEZIA_BASE_CLASS QApplication #define AMNEZIA_BASE_CLASS QApplication
#define QAPPLICATION_CLASS QApplication
#endif #endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS class AmneziaApplication : public AMNEZIA_BASE_CLASS
{ {
Q_OBJECT Q_OBJECT
public: public:
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]); AmneziaApplication(int &argc, char *argv[]);
#else
AmneziaApplication(int &argc, char *argv[]);
#endif
virtual ~AmneziaApplication(); virtual ~AmneziaApplication();
void init(); void init();
@@ -70,10 +75,6 @@ public:
void updateTranslator(const QLocale &locale); void updateTranslator(const QLocale &locale);
bool parseCommands(); bool parseCommands();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
void startLocalServer();
#endif
QQmlApplicationEngine *qmlEngine() const; QQmlApplicationEngine *qmlEngine() const;
QNetworkAccessManager *manager() { return m_nam; } QNetworkAccessManager *manager() { return m_nam; }
+12 -5
View File
@@ -1,21 +1,28 @@
package org.amnezia.vpn.protocol.awg package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.wireguard.Wireguard import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.protocol.wireguard.WireguardConfig import org.amnezia.vpn.util.optStringOrNull
import org.json.JSONObject import org.json.JSONObject
class Awg : Wireguard() { class Awg : Wireguard() {
override val ifName: String = "awg0" override val ifName: String = "awg0"
override fun parseConfig(config: JSONObject): WireguardConfig { override fun parseConfig(config: JSONObject): AwgConfig {
val configData = config.getJSONObject("awg_config_data") val configData = config.getJSONObject("awg_config_data")
return WireguardConfig.build { return AwgConfig.build {
setUseProtocolExtension(true)
configExtensionParameters(configData)
configWireguard(config, configData) configWireguard(config, configData)
configSplitTunneling(config) configSplitTunneling(config)
configAppSplitTunneling(config) configAppSplitTunneling(config)
configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
} }
} }
} }
@@ -0,0 +1,108 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
class AwgConfig private constructor(
wireguardConfigBuilder: WireguardConfig.Builder,
val jc: Int,
val jmin: Int,
val jmax: Int,
val s1: Int,
val s2: Int,
val h1: Long,
val h2: Long,
val h3: Long,
val h4: Long
) : WireguardConfig(wireguardConfigBuilder) {
private constructor(builder: Builder) : this(
builder,
builder.jc,
builder.jmin,
builder.jmax,
builder.s1,
builder.s2,
builder.h1,
builder.h2,
builder.h3,
builder.h4
)
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
super.appendDeviceLine(this)
appendLine("jc=$jc")
appendLine("jmin=$jmin")
appendLine("jmax=$jmax")
appendLine("s1=$s1")
appendLine("s2=$s2")
appendLine("h1=$h1")
appendLine("h2=$h2")
appendLine("h3=$h3")
appendLine("h4=$h4")
}
class Builder : WireguardConfig.Builder() {
private var _jc: Int? = null
internal var jc: Int
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
private set(value) { _jc = value }
private var _jmin: Int? = null
internal var jmin: Int
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
private set(value) { _jmin = value }
private var _jmax: Int? = null
internal var jmax: Int
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
private set(value) { _jmax = value }
private var _s1: Int? = null
internal var s1: Int
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
private set(value) { _s1 = value }
private var _s2: Int? = null
internal var s2: Int
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
private set(value) { _s2 = value }
private var _h1: Long? = null
internal var h1: Long
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
private set(value) { _h1 = value }
private var _h2: Long? = null
internal var h2: Long
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
private set(value) { _h2 = value }
private var _h3: Long? = null
internal var h3: Long
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
private set(value) { _h3 = value }
private var _h4: Long? = null
internal var h4: Long
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
private set(value) { _h4 = value }
fun setJc(jc: Int) = apply { this.jc = jc }
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 }
override fun build(): AwgConfig = configBuild().run { AwgConfig(this@Builder) }
}
companion object {
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
}
}
@@ -3,16 +3,10 @@ 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.util.LibraryLoader.loadSharedLibrary
import org.json.JSONObject import org.json.JSONObject
class Cloak : OpenVpn() { class Cloak : OpenVpn() {
override fun internalInit() {
super.internalInit()
if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
}
override fun parseConfig(config: JSONObject): ClientAPI_Config { override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config() val openVpnConfig = ClientAPI_Config()
+1 -1
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 # For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34 #androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0 #androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=26 #qtMinSdkVersion=24
#qtTargetSdkVersion=34 #qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125 #androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64 #qtTargetAbiList=x86_64
@@ -11,7 +11,6 @@ import org.amnezia.vpn.protocol.Protocol
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.LibraryLoader.loadSharedLibrary
import org.amnezia.vpn.util.net.InetNetwork 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.amnezia.vpn.util.net.parseInetAddress
@@ -35,10 +34,7 @@ open class OpenVpn : Protocol() {
} }
override fun internalInit() { override fun internalInit() {
if (!isInitialized) { if (!isInitialized) loadSharedLibrary(context, "ovpn3")
loadSharedLibrary(context, "ovpn3")
loadSharedLibrary(context, "ovpnutil")
}
if (this::scope.isInitialized) { if (this::scope.isInitialized) {
scope.cancel() scope.cancel()
} }
@@ -2,6 +2,7 @@ package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
@@ -1,5 +1,6 @@
package org.amnezia.vpn.protocol package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.IpPrefix import android.net.IpPrefix
import android.net.VpnService import android.net.VpnService
@@ -7,6 +8,9 @@ import android.net.VpnService.Builder
import android.os.Build import android.os.Build
import android.system.OsConstants import android.system.OsConstants
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.InetNetwork
@@ -154,6 +158,60 @@ abstract class Protocol {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false) vpnBuilder.setMetered(false)
} }
companion object {
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
Log.d(TAG, "Extracting library: $libraryName")
val apks = hashSetOf<String>()
context.applicationInfo.run {
sourceDir?.let { apks += it }
splitSourceDirs?.let { apks += it }
}
for (abi in Build.SUPPORTED_ABIS) {
for (apk in apks) {
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
val mappedName = System.mapLibraryName(libraryName)
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
val zipEntry = zipFile.getEntry(libraryZipPath)
zipEntry?.let {
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
FileOutputStream(destination).use { outStream ->
zipFile.getInputStream(zipEntry).use { inStream ->
inStream.copyTo(outStream, 32 * 1024)
outStream.fd.sync()
}
}
}
return true
}
}
}
return false
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
fun loadSharedLibrary(context: Context, libraryName: String) {
Log.d(TAG, "Loading library: $libraryName")
try {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.d(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
if (extractLibrary(context, libraryName, tempFile)) {
System.load(tempFile.absolutePath)
return
}
} catch (e: Exception) {
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
} finally {
tempFile?.delete()
}
}
}
} }
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask) private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
+1
View File
@@ -3,6 +3,7 @@
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. --> <!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
<array name="bundled_libs"> <array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array> </array>
<array name="qt_libs"> <array name="qt_libs">
@@ -21,7 +21,6 @@ import android.os.Looper
import android.os.Message import android.os.Message
import android.os.Messenger import android.os.Messenger
import android.provider.Settings import android.provider.Settings
import android.view.MotionEvent
import android.view.WindowManager.LayoutParams import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
@@ -44,7 +43,6 @@ import kotlinx.coroutines.withContext
import org.amnezia.vpn.protocol.getStatistics 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.LibraryLoader.loadSharedLibrary
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.json.JSONException import org.json.JSONException
@@ -159,8 +157,7 @@ class AmneziaActivity : QtActivity() {
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity") Log.d(TAG, "Create Amnezia activity: $intent")
loadLibs()
window.apply { window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = getColor(R.color.black) statusBarColor = getColor(R.color.black)
@@ -182,17 +179,6 @@ class AmneziaActivity : QtActivity() {
runBlocking { vpnProto = proto.await() } runBlocking { vpnProto = proto.await() }
} }
private fun loadLibs() {
listOf(
"rsapss",
"crypto_3",
"ssl_3",
"ssh"
).forEach {
loadSharedLibrary(this.applicationContext, it)
}
}
private fun registerBroadcastReceivers() { private fun registerBroadcastReceivers() {
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver( registerBroadcastReceiver(
@@ -201,7 +187,7 @@ class AmneziaActivity : QtActivity() {
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
) )
) { ) {
Log.v( Log.d(
TAG, "Notification state changed: ${it?.action}, blocked = " + TAG, "Notification state changed: ${it?.action}, blocked = " +
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}" "${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
) )
@@ -215,7 +201,7 @@ class AmneziaActivity : QtActivity() {
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.v(TAG, "onNewIntent: $intent") Log.d(TAG, "onNewIntent: $intent")
intent?.let(::processIntent) intent?.let(::processIntent)
} }
@@ -404,7 +390,7 @@ class AmneziaActivity : QtActivity() {
@MainThread @MainThread
private fun startVpn(vpnConfig: String) { private fun startVpn(vpnConfig: String) {
getVpnProto(vpnConfig)?.let { proto -> getVpnProto(vpnConfig)?.let { proto ->
Log.v(TAG, "Proto from config: $proto, current proto: $vpnProto") Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
if (isServiceConnected) { if (isServiceConnected) {
if (proto.serviceClass == vpnProto?.serviceClass) { if (proto.serviceClass == vpnProto?.serviceClass) {
vpnProto = proto vpnProto = proto
@@ -517,7 +503,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler( startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = { onSuccess = {
it?.data?.let { uri -> it?.data?.let { uri ->
Log.v(TAG, "Save file to $uri") Log.d(TAG, "Save file to $uri")
try { try {
contentResolver.openOutputStream(uri)?.use { os -> contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) } os.bufferedWriter().use { it.write(data) }
@@ -566,7 +552,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler( startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
onAny = { onAny = {
val uri = it?.data?.toString() ?: "" val uri = it?.data?.toString() ?: ""
Log.v(TAG, "Open file: $uri") Log.d(TAG, "Open file: $uri")
mainScope.launch { mainScope.launch {
qtInitialized.await() qtInitialized.await()
QtAndroidController.onFileOpened(uri) QtAndroidController.onFileOpened(uri)
@@ -721,66 +707,6 @@ 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 * Utils methods
*/ */
@@ -22,7 +22,6 @@ import androidx.annotation.MainThread
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import java.net.UnknownHostException
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
@@ -41,6 +40,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import org.amnezia.vpn.protocol.BadConfigException import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.LoadLibraryException
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
@@ -50,7 +50,6 @@ import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
import org.amnezia.vpn.protocol.VpnException import org.amnezia.vpn.protocol.VpnException
import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.protocol.putStatus import org.amnezia.vpn.protocol.putStatus
import org.amnezia.vpn.util.LoadLibraryException
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
@@ -128,8 +127,6 @@ open class AmneziaVpnService : VpnService() {
is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}") is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}")
is UnknownHostException -> onError("Unknown host")
else -> throw e else -> throw e
} }
} }
@@ -300,7 +297,7 @@ open class AmneziaVpnService : VpnService() {
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) { ) {
it?.action?.let { action -> it?.action?.let { action ->
Log.v(TAG, "Broadcast request received: $action") Log.d(TAG, "Broadcast request received: $action")
when (action) { when (action) {
ACTION_CONNECT -> connect() ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect() ACTION_DISCONNECT -> disconnect()
@@ -317,7 +314,7 @@ open class AmneziaVpnService : VpnService() {
) )
) { ) {
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false) val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state") Log.d(TAG, "Notification state changed: ${it?.action}, blocked = $state")
if (state == false) { if (state == false) {
enableNotification() enableNotification()
} else { } else {
@@ -450,7 +447,7 @@ open class AmneziaVpnService : VpnService() {
serviceNotification.isNotificationEnabled() && serviceNotification.isNotificationEnabled() &&
getSystemService<PowerManager>()?.isInteractive != false getSystemService<PowerManager>()?.isInteractive != false
) { ) {
Log.v(TAG, "Launch traffic stats update") Log.d(TAG, "Launch traffic stats update")
trafficStats.reset() trafficStats.reset()
startTrafficStatsUpdateJob() startTrafficStatsUpdateJob()
} }
@@ -66,7 +66,7 @@ class AuthActivity : FragmentActivity() {
object : BiometricPrompt.AuthenticationCallback() { object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: AuthenticationResult) { override fun onAuthenticationSucceeded(result: AuthenticationResult) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
Log.v(TAG, "Authentication succeeded") Log.d(TAG, "Authentication succeeded")
QtAndroidController.onAuthResult(true) QtAndroidController.onAuthResult(true)
finish() finish()
} }
@@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.v(TAG, "Create Import Config Activity: $intent") Log.d(TAG, "Create Import Config Activity: $intent")
intent?.let(::readConfig) intent?.let(::readConfig)
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.v(TAG, "onNewIntent: $intent") Log.d(TAG, "onNewIntent: $intent")
intent.let(::readConfig) intent.let(::readConfig)
} }
private fun readConfig(intent: Intent) { private fun readConfig(intent: Intent) {
when (intent.action) { when (intent.action) {
ACTION_SEND -> { ACTION_SEND -> {
Log.v(TAG, "Process SEND action, type: ${intent.type}") Log.d(TAG, "Process SEND action, type: ${intent.type}")
when (intent.type) { when (intent.type) {
"application/octet-stream" -> { "application/octet-stream" -> {
intent.getUriCompat()?.let { uri -> intent.getUriCompat()?.let { uri ->
@@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
} }
ACTION_VIEW -> { ACTION_VIEW -> {
Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}") Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
when (intent.scheme) { when (intent.scheme) {
"file", "content" -> { "file", "content" -> {
intent.data?.let { uri -> intent.data?.let { uri ->
@@ -62,7 +62,7 @@ class ServiceNotification(private val context: Context) {
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification { fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null val speedString = if (state == CONNECTED) zeroSpeed else null
Log.v(TAG, "Build notification: $serverName, $state") Log.d(TAG, "Build notification: $serverName, $state")
return notificationBuilder return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round) .setSmallIcon(R.drawable.ic_amnezia_round)
@@ -88,15 +88,17 @@ class ServiceNotification(private val context: Context) {
fun isNotificationEnabled(): Boolean { fun isNotificationEnabled(): Boolean {
if (!context.isNotificationPermissionGranted()) return false if (!context.isNotificationPermissionGranted()) return false
if (!notificationManager.areNotificationsEnabled()) return false if (!notificationManager.areNotificationsEnabled()) return false
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
it.importance != NotificationManager.IMPORTANCE_NONE return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
} ?: true ?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true
}
return true
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) { fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
if (context.isNotificationPermissionGranted()) { if (context.isNotificationPermissionGranted()) {
Log.v(TAG, "Update notification: $serverName, $state") Log.d(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state)) notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
} }
} }
@@ -1,66 +0,0 @@
package org.amnezia.vpn.util
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
private const val TAG = "LibraryLoader"
object LibraryLoader {
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
Log.d(TAG, "Extracting library: $libraryName")
val apks = hashSetOf<String>()
context.applicationInfo.run {
sourceDir?.let { apks += it }
splitSourceDirs?.let { apks += it }
}
for (abi in Build.SUPPORTED_ABIS) {
for (apk in apks) {
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
val mappedName = System.mapLibraryName(libraryName)
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
val zipEntry = zipFile.getEntry(libraryZipPath)
zipEntry?.let {
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
FileOutputStream(destination).use { outStream ->
zipFile.getInputStream(zipEntry).use { inStream ->
inStream.copyTo(outStream, 32 * 1024)
outStream.fd.sync()
}
}
}
return true
}
}
}
return false
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
fun loadSharedLibrary(context: Context, libraryName: String) {
Log.d(TAG, "Loading library: $libraryName")
try {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.w(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
if (extractLibrary(context, libraryName, tempFile)) {
System.load(tempFile.absolutePath)
return
}
} catch (e: Exception) {
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
} finally {
tempFile?.delete()
}
}
}
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
+15 -2
View File
@@ -1,6 +1,8 @@
package org.amnezia.vpn.util package org.amnezia.vpn.util
import android.content.Context import android.content.Context
import android.icu.text.DateFormat
import android.icu.text.SimpleDateFormat
import android.os.Build import android.os.Build
import android.os.Process import android.os.Process
import java.io.File import java.io.File
@@ -10,6 +12,8 @@ import java.nio.channels.FileChannel
import java.nio.channels.FileLock import java.nio.channels.FileLock
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E import org.amnezia.vpn.util.Log.Priority.E
@@ -37,7 +41,11 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
* | | | create a report and/or terminate the process | * | | | create a report and/or terminate the process |
*/ */
object Log { object Log {
private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN) private val dateTimeFormat: Any =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
else object : ThreadLocal<DateFormat>() {
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
}
private lateinit var logDir: File private lateinit var logDir: File
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) } private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
@@ -135,7 +143,12 @@ object Log {
} }
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String { private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val date = LocalDateTime.now().format(dateTimeFormat) val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter)
} else {
@Suppress("UNCHECKED_CAST")
(dateTimeFormat as ThreadLocal<DateFormat>).get()?.format(Date())
}
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " + return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n" "$tag: $msg\n"
} }
@@ -42,12 +42,18 @@ class NetworkState(
private val networkCallback: NetworkCallback by lazy(NONE) { private val networkCallback: NetworkCallback by lazy(NONE) {
object : NetworkCallback() { object : NetworkCallback() {
override fun onAvailable(network: Network) { override fun onAvailable(network: Network) {
Log.v(TAG, "onAvailable: $network") Log.d(TAG, "onAvailable: $network")
} }
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities") Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
checkNetworkState(network, networkCapabilities) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
checkNetworkState(network, networkCapabilities)
} else {
handler.post {
checkNetworkState(network, networkCapabilities)
}
}
} }
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) { private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
@@ -67,11 +73,11 @@ class NetworkState(
} }
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) { override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
Log.v(TAG, "onBlockedStatusChanged: $network, $blocked") Log.d(TAG, "onBlockedStatusChanged: $network, $blocked")
} }
override fun onLost(network: Network) { override fun onLost(network: Network) {
Log.v(TAG, "onLost: $network") Log.d(TAG, "onLost: $network")
} }
} }
} }
@@ -81,7 +87,7 @@ class NetworkState(
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 { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val numberAttempts = 300 val numberAttempts = 300
var attemptCount = 0 var attemptCount = 0
while(true) { while(true) {
@@ -102,6 +108,8 @@ class NetworkState(
} }
} }
} }
} else {
connectivityManager.requestNetwork(networkRequest, networkCallback)
} }
isListenerBound = true isListenerBound = true
} }
@@ -3,6 +3,7 @@ package org.amnezia.vpn.protocol.wireguard
import android.net.VpnService.Builder import android.net.VpnService.Builder
import java.io.IOException import java.io.IOException
import java.util.Locale import java.util.Locale
import java.util.TreeMap
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -12,7 +13,6 @@ import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
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.LibraryLoader.loadSharedLibrary
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.asSequence import org.amnezia.vpn.util.asSequence
import org.amnezia.vpn.util.net.InetEndpoint import org.amnezia.vpn.util.net.InetEndpoint
@@ -66,7 +66,7 @@ open class Wireguard : Protocol() {
try { try {
delay(1000) delay(1000)
var log = getLogcat(time) var log = getLogcat(time)
Log.v(TAG, "First waiting log: $log") Log.d(TAG, "First waiting log: $log")
// check that there is a connection log, // check that there is a connection log,
// to avoid infinite connection // to avoid infinite connection
if (!log.contains("Attaching to interface")) { if (!log.contains("Attaching to interface")) {
@@ -129,29 +129,12 @@ open class Wireguard : Protocol() {
val port = configData.getInt("port") val port = configData.getInt("port")
setEndpoint(InetEndpoint(host, port)) setEndpoint(InetEndpoint(host, port))
if (configData.optBoolean("isObfuscationEnabled")) {
setUseProtocolExtension(true)
configExtensionParameters(configData)
}
configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) } configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) } configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) } configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) } configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
} }
protected fun WireguardConfig.Builder.configExtensionParameters(configData: JSONObject) {
configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
}
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
if (tunnelHandle != -1) { if (tunnelHandle != -1) {
Log.w(TAG, "Tunnel already up") Log.w(TAG, "Tunnel already up")
@@ -1,7 +1,6 @@
package org.amnezia.vpn.protocol.wireguard package org.amnezia.vpn.protocol.wireguard
import android.util.Base64 import android.util.Base64
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.ProtocolConfig import org.amnezia.vpn.protocol.ProtocolConfig
import org.amnezia.vpn.util.net.InetEndpoint import org.amnezia.vpn.util.net.InetEndpoint
@@ -13,17 +12,7 @@ open class WireguardConfig protected constructor(
val persistentKeepalive: Int, val persistentKeepalive: Int,
val publicKeyHex: String, val publicKeyHex: String,
val preSharedKeyHex: String?, val preSharedKeyHex: String?,
val privateKeyHex: String, val privateKeyHex: String
val useProtocolExtension: Boolean,
val jc: Int?,
val jmin: Int?,
val jmax: Int?,
val s1: Int?,
val s2: Int?,
val h1: Long?,
val h2: Long?,
val h3: Long?,
val h4: Long?
) : ProtocolConfig(protocolConfigBuilder) { ) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this( protected constructor(builder: Builder) : this(
@@ -32,17 +21,7 @@ open class WireguardConfig protected constructor(
builder.persistentKeepalive, builder.persistentKeepalive,
builder.publicKeyHex, builder.publicKeyHex,
builder.preSharedKeyHex, builder.preSharedKeyHex,
builder.privateKeyHex, builder.privateKeyHex
builder.useProtocolExtension,
builder.jc,
builder.jmin,
builder.jmax,
builder.s1,
builder.s2,
builder.h1,
builder.h2,
builder.h3,
builder.h4
) )
fun toWgUserspaceString(): String = with(StringBuilder()) { fun toWgUserspaceString(): String = with(StringBuilder()) {
@@ -54,30 +33,6 @@ open class WireguardConfig protected constructor(
open fun appendDeviceLine(sb: StringBuilder) = with(sb) { open fun appendDeviceLine(sb: StringBuilder) = with(sb) {
appendLine("private_key=$privateKeyHex") appendLine("private_key=$privateKeyHex")
if (useProtocolExtension) {
validateProtocolExtensionParameters()
appendLine("jc=$jc")
appendLine("jmin=$jmin")
appendLine("jmax=$jmax")
appendLine("s1=$s1")
appendLine("s2=$s2")
appendLine("h1=$h1")
appendLine("h2=$h2")
appendLine("h3=$h3")
appendLine("h4=$h4")
}
}
private fun validateProtocolExtensionParameters() {
if (jc == null) throw BadConfigException("Parameter jc is undefined")
if (jmin == null) throw BadConfigException("Parameter jmin is undefined")
if (jmax == null) throw BadConfigException("Parameter jmax is undefined")
if (s1 == null) throw BadConfigException("Parameter s1 is undefined")
if (s2 == null) throw BadConfigException("Parameter s2 is undefined")
if (h1 == null) throw BadConfigException("Parameter h1 is undefined")
if (h2 == null) throw BadConfigException("Parameter h2 is undefined")
if (h3 == null) throw BadConfigException("Parameter h3 is undefined")
if (h4 == null) throw BadConfigException("Parameter h4 is undefined")
} }
open fun appendPeerLine(sb: StringBuilder) = with(sb) { open fun appendPeerLine(sb: StringBuilder) = with(sb) {
@@ -110,18 +65,6 @@ open class WireguardConfig protected constructor(
override var mtu: Int = WIREGUARD_DEFAULT_MTU override var mtu: Int = WIREGUARD_DEFAULT_MTU
internal var useProtocolExtension: Boolean = false
internal var jc: Int? = null
internal var jmin: Int? = null
internal var jmax: Int? = null
internal var s1: Int? = null
internal var s2: Int? = null
internal var h1: Long? = null
internal var h2: Long? = null
internal var h3: Long? = null
internal var h4: Long? = null
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint } fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive } fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive }
@@ -132,18 +75,6 @@ open class WireguardConfig protected constructor(
fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex } fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex }
fun setUseProtocolExtension(useProtocolExtension: Boolean) = apply { this.useProtocolExtension = useProtocolExtension }
fun setJc(jc: Int) = apply { this.jc = jc }
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 }
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) } override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
} }
+2 -2
View File
@@ -130,8 +130,8 @@ class Xray : Protocol() {
LibXray.initXray(assetsPath) LibXray.initXray(assetsPath)
val geoDir = File(assetsPath, "geo").absolutePath val geoDir = File(assetsPath, "geo").absolutePath
val configPath = File(context.cacheDir, "config.json") val configPath = File(context.cacheDir, "config.json")
Log.v(TAG, "xray.location.asset: $geoDir") Log.d(TAG, "xray.location.asset: $geoDir")
Log.v(TAG, "config: $configPath") Log.d(TAG, "config: $configPath")
try { try {
configPath.writeText(configJson) configPath.writeText(configJson)
} catch (e: IOException) { } catch (e: IOException) {
+4
View File
@@ -2,6 +2,10 @@ set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
if(NOT IOS AND NOT ANDROID)
include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake)
endif()
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel) add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)
set(LIBS ${LIBS} SortFilterProxyModel) set(LIBS ${LIBS} SortFilterProxyModel)
include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake) include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
+1 -1
View File
@@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build") message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 26) set(APP_ANDROID_MIN_SDK 24)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE) "The minimum API level supported by the application or library" FORCE)
@@ -199,7 +199,7 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
jConfig[config_key::server_pub_key] = connData.serverPubKey; jConfig[config_key::server_pub_key] = connData.serverPubKey;
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
jConfig[config_key::persistent_keep_alive] = "25"; jConfig[config_key::persistent_keep_alive] = 25;
QJsonArray allowedIps { "0.0.0.0/0", "::/0" }; QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
jConfig[config_key::allowed_ips] = allowedIps; jConfig[config_key::allowed_ips] = allowedIps;
+40 -117
View File
@@ -1,8 +1,5 @@
#include "apiController.h" #include "apiController.h"
#include <algorithm>
#include <random>
#include <QEventLoop> #include <QEventLoop>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
@@ -14,7 +11,6 @@
#include "amnezia_application.h" #include "amnezia_application.h"
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "core/enums/apiEnums.h" #include "core/enums/apiEnums.h"
#include "utilities.h"
#include "version.h" #include "version.h"
namespace namespace
@@ -37,7 +33,6 @@ namespace
constexpr char userCountryCode[] = "user_country_code"; constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code"; constexpr char serverCountryCode[] = "server_country_code";
constexpr char serviceType[] = "service_type"; constexpr char serviceType[] = "service_type";
constexpr char serviceInfo[] = "service_info";
constexpr char aesKey[] = "aes_key"; constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv"; constexpr char aesIv[] = "aes_iv";
@@ -45,11 +40,10 @@ namespace
constexpr char apiPayload[] = "api_payload"; constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload"; constexpr char keyPayload[] = "key_payload";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
} }
const QStringList proxyStorageUrl = { "" };
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply) ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{ {
if (!sslErrors.empty()) { if (!sslErrors.empty()) {
@@ -69,28 +63,6 @@ namespace
return ErrorCode::ApiConfigDownloadError; return ErrorCode::ApiConfigDownloadError;
} }
} }
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
const QByteArray &iv = "", const QByteArray &salt = "")
{
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << "Timeout occurred";
return true;
} else if (responseBody.contains("html")) {
qDebug() << "The response contains an html tag";
return true;
} else if (checkEncryption) {
try {
QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
} catch (...) {
qDebug() << "Failed to decrypt the data";
return true;
}
}
return false;
}
} }
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent) ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
@@ -122,8 +94,8 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) { } else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = newServerConfig.value(config_key::containers).toArray(); auto containers = serverConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) { if (containers.isEmpty()) {
return; // todo process error return; // todo process error
} }
@@ -142,35 +114,25 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader); containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig; container[containerName] = containerConfig;
containers.replace(0, container); containers.replace(0, container);
newServerConfig[config_key::containers] = containers; serverConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(newServerConfig).toJson()); configStr = QString(QJsonDocument(serverConfig).toJson());
} }
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1); serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2); serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2);
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers); serverConfig[config_key::containers] = apiConfig.value(config_key::containers);
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName); serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName);
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion); serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion);
serverConfig[config_key::description] = newServerConfig.value(config_key::description); serverConfig[config_key::description] = apiConfig.value(config_key::description);
serverConfig[config_key::name] = newServerConfig.value(config_key::name); serverConfig[config_key::name] = apiConfig.value(config_key::name);
} }
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString(); auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer; 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);
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
}
serverConfig[configKey::apiConfig] = apiConfig;
return; return;
} }
@@ -184,15 +146,6 @@ QStringList ApiController::getProxyUrls()
QList<QSslError> sslErrors; QList<QSslError> sslErrors;
QNetworkReply *reply; 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) { for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl); request.setUrl(proxyStorageUrl);
reply = amnApp->manager()->get(request); reply = amnApp->manager()->get(request);
@@ -213,23 +166,11 @@ QStringList ApiController::getProxyUrls()
EVP_PKEY *privateKey = nullptr; EVP_PKEY *privateKey = nullptr;
QByteArray responseBody; QByteArray responseBody;
try { try {
if (!m_isDevEnvironment) { QByteArray key = PROD_PROXY_STORAGE_KEY;
QCryptographicHash hash(QCryptographicHash::Sha512); QSimpleCrypto::QRsa rsa;
hash.addData(key); privateKey = rsa.getPrivateKeyFromByteArray(key, "");
QByteArray hashResult = hash.result().toHex(); responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING);
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 (...) { } catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload"; qCritical() << "error loading private key from environment variables or decrypting payload";
return {}; return {};
} }
@@ -351,44 +292,38 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
responseBody = reply->readAll(); if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
m_proxyUrls = getProxyUrls(); m_proxyUrls = getProxyUrls();
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
for (const QString &proxyUrl : m_proxyUrls) { for (const QString &proxyUrl : m_proxyUrls) {
qDebug() << "Go to the next endpoint";
request.setUrl(QString("%1v1/services").arg(proxyUrl)); request.setUrl(QString("%1v1/services").arg(proxyUrl));
reply->deleteLater(); // delete the previous reply
reply = amnApp->manager()->get(request); reply = amnApp->manager()->get(request);
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
responseBody = reply->readAll(); && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
break; break;
} }
reply->deleteLater();
} }
} }
responseBody = reply->readAll();
auto errorCode = checkErrors(sslErrors, reply); auto errorCode = checkErrors(sslErrors, reply);
reply->deleteLater(); reply->deleteLater();
return errorCode; return errorCode;
} }
ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig)
QJsonObject &serverConfig)
{ {
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess(); IosController::Instance()->requestInetAccess();
QThread::msleep(10); QThread::msleep(10);
#endif #endif
QNetworkAccessManager manager;
QNetworkRequest request; QNetworkRequest request;
request.setTransferTimeout(7000); request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
@@ -404,9 +339,6 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
} }
apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
if (!authData.isEmpty()) {
apiPayload[configKey::authData] = authData;
}
QSimpleCrypto::QBlockCipher blockCipher; QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32); QByteArray key = blockCipher.generatePrivateSalt(32);
@@ -425,11 +357,10 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
EVP_PKEY *publicKey = nullptr; EVP_PKEY *publicKey = nullptr;
try { try {
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY; QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa; QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey); publicKey = rsa.getPublicKeyFromByteArray(key);
} catch (...) { } catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables"; qCritical() << "error loading public key from environment variables";
return ErrorCode::ApiMissingAgwPublicKey; return ErrorCode::ApiMissingAgwPublicKey;
} }
@@ -439,16 +370,14 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt); encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
} catch (...) { // todo change error handling in QSimpleCrypto? } catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when encrypting the request body"; qCritical() << "error when encrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
} }
QJsonObject requestBody; QJsonObject requestBody;
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64()); requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64()); requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson()); QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
QEventLoop wait; QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
@@ -457,43 +386,37 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
auto encryptedResponseBody = reply->readAll(); if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
if (m_proxyUrls.isEmpty()) {
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) { m_proxyUrls = getProxyUrls();
m_proxyUrls = getProxyUrls(); }
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
for (const QString &proxyUrl : m_proxyUrls) { for (const QString &proxyUrl : m_proxyUrls) {
qDebug() << "Go to the next endpoint";
request.setUrl(QString("%1v1/config").arg(proxyUrl)); request.setUrl(QString("%1v1/config").arg(proxyUrl));
reply->deleteLater(); // delete the previous reply reply = manager.post(request, QJsonDocument(requestBody).toJson());
reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
encryptedResponseBody = reply->readAll(); && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
break; break;
} }
reply->deleteLater();
} }
} }
auto errorCode = checkErrors(sslErrors, reply); auto errorCode = checkErrors(sslErrors, reply);
reply->deleteLater();
if (errorCode) { if (errorCode) {
return errorCode; return errorCode;
} }
auto encryptedResponseBody = reply->readAll();
reply->deleteLater();
try { try {
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt); auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig); fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
} catch (...) { // todo change error handling in QSimpleCrypto? } catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body"; qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
} }
return errorCode; return errorCode;
+1 -1
View File
@@ -21,7 +21,7 @@ public slots:
ErrorCode getServicesList(QByteArray &responseBody); ErrorCode getServicesList(QByteArray &responseBody);
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig); const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig);
signals: signals:
void errorOccurred(ErrorCode errorCode); void errorOccurred(ErrorCode errorCode);
@@ -770,12 +770,6 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
if (!stdOut.contains("sudo")) if (!stdOut.contains("sudo"))
return ErrorCode::ServerUserNotInSudo; return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("command not found"))
return ErrorCode::SudoPackageIsNotPreinstalled;
if (stdOut.contains("sudoers"))
return ErrorCode::ServerUserNotListedInSudoers;
if (stdOut.contains("password is required"))
return ErrorCode::ServerUserPasswordRequired;
return error; return error;
} }
-5
View File
@@ -56,9 +56,6 @@ namespace amnezia
ServerCancelInstallation = 204, ServerCancelInstallation = 204,
ServerUserNotInSudo = 205, ServerUserNotInSudo = 205,
ServerPacketManagerError = 206, ServerPacketManagerError = 206,
SudoPackageIsNotPreinstalled = 207,
ServerUserNotListedInSudoers = 208,
ServerUserPasswordRequired = 209,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,
@@ -99,7 +96,6 @@ namespace amnezia
// import and install errors // import and install errors
ImportInvalidConfigError = 900, ImportInvalidConfigError = 900,
ImportOpenConfigError = 901,
// Android errors // Android errors
AndroidError = 1000, AndroidError = 1000,
@@ -111,7 +107,6 @@ namespace amnezia
ApiConfigTimeoutError = 1103, ApiConfigTimeoutError = 1103,
ApiConfigSslError = 1104, ApiConfigSslError = 1104,
ApiMissingAgwPublicKey = 1105, ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
// QFile errors // QFile errors
OpenError = 1200, OpenError = 1200,
-5
View File
@@ -21,9 +21,6 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; case(ErrorCode::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(ErrorCode::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(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
case(ErrorCode::ServerUserNotListedInSudoers): errorMessage = QObject::tr("The user is not listed in sudoers"); break;
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
// Libssh errors // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
@@ -53,7 +50,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; 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::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 // Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break; case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
@@ -65,7 +61,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break; 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::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); 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 // QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
+2 -14
View File
@@ -29,12 +29,6 @@ QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
return Instance()->m_ipcClient; return Instance()->m_ipcClient;
} }
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
{
if (!Instance()) return nullptr;
return Instance()->m_Tun2SocksClient;
}
bool IpcClient::init(IpcClient *instance) bool IpcClient::init(IpcClient *instance)
{ {
m_instance = instance; m_instance = instance;
@@ -50,12 +44,6 @@ bool IpcClient::init(IpcClient *instance)
qWarning() << "IpcClient replica is not connected!"; qWarning() << "IpcClient replica is not connected!";
} }
Instance()->m_Tun2SocksClient.reset(Instance()->m_ClientNode.acquire<IpcProcessTun2SocksReplica>());
Instance()->m_Tun2SocksClient->waitForSource(1000);
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
qWarning() << "IpcClient::m_Tun2SocksClient replica is not connected!";
}
}); });
connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){ connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){
@@ -63,16 +51,16 @@ bool IpcClient::init(IpcClient *instance)
}); });
Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl()); Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
Instance()->m_localSocket->waitForConnected(); Instance()->m_localSocket->waitForConnected();
if (!Instance()->m_ipcClient) { if (!Instance()->m_ipcClient) {
qDebug() << "IpcClient::init failed"; qDebug() << "IpcClient::init failed";
return false; return false;
} }
qDebug() << "IpcClient::init succeed"; qDebug() << "IpcClient::init succeed";
return (Instance()->m_ipcClient->isReplicaValid() && Instance()->m_Tun2SocksClient->isReplicaValid()); return Instance()->m_ipcClient->isReplicaValid();
} }
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess() QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
-5
View File
@@ -6,7 +6,6 @@
#include "ipc.h" #include "ipc.h"
#include "rep_ipc_interface_replica.h" #include "rep_ipc_interface_replica.h"
#include "rep_ipc_process_tun2socks_replica.h"
#include "privileged_process.h" #include "privileged_process.h"
@@ -19,7 +18,6 @@ public:
static IpcClient *Instance(); static IpcClient *Instance();
static bool init(IpcClient *instance); static bool init(IpcClient *instance);
static QSharedPointer<IpcInterfaceReplica> Interface(); static QSharedPointer<IpcInterfaceReplica> Interface();
static QSharedPointer<IpcProcessTun2SocksReplica> InterfaceTun2Socks();
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess(); static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
bool isSocketConnected() const; bool isSocketConnected() const;
@@ -30,11 +28,8 @@ private:
~IpcClient() override; ~IpcClient() override;
QRemoteObjectNode m_ClientNode; QRemoteObjectNode m_ClientNode;
QRemoteObjectNode m_Tun2SocksNode;
QSharedPointer<IpcInterfaceReplica> m_ipcClient; QSharedPointer<IpcInterfaceReplica> m_ipcClient;
QPointer<QLocalSocket> m_localSocket; QPointer<QLocalSocket> m_localSocket;
QPointer<QLocalSocket> m_tun2socksSocket;
QSharedPointer<IpcProcessTun2SocksReplica> m_Tun2SocksClient;
struct ProcessDescriptor { struct ProcessDescriptor {
ProcessDescriptor () { ProcessDescriptor () {
+18 -5
View File
@@ -78,7 +78,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
return false; return false;
} }
if (!dnsutils()->restoreResolvers()) { if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
return false; return false;
} }
@@ -165,6 +165,10 @@ bool Daemon::activate(const InterfaceConfig& config) {
} }
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) { bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
if (!supportDnsUtils()) {
return true;
}
if ((config.m_hopType == InterfaceConfig::MultiHopExit) || if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
(config.m_hopType == InterfaceConfig::SingleHop)) { (config.m_hopType == InterfaceConfig::SingleHop)) {
QList<QHostAddress> resolvers; QList<QHostAddress> resolvers;
@@ -419,8 +423,13 @@ bool Daemon::deactivate(bool emitSignals) {
} }
// Cleanup DNS // Cleanup DNS
if (!dnsutils()->restoreResolvers()) { if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
logger.warning() << "Failed to restore DNS resolvers."; return false;
}
if (!wgutils()->interfaceExists()) {
logger.warning() << "Wireguard interface does not exist.";
return false;
} }
// Cleanup peers and routing // Cleanup peers and routing
@@ -440,9 +449,13 @@ bool Daemon::deactivate(bool emitSignals) {
} }
m_excludedAddrSet.clear(); m_excludedAddrSet.clear();
m_connections.clear();
// Delete the interface // Delete the interface
return wgutils()->deleteInterface(); if (!wgutils()->deleteInterface()) {
return false;
}
m_connections.clear();
return true;
} }
QString Daemon::logs() { QString Daemon::logs() {
+1
View File
@@ -69,6 +69,7 @@ class Daemon : public QObject {
virtual WireguardUtils* wgutils() const = 0; virtual WireguardUtils* wgutils() const = 0;
virtual bool supportIPUtils() const { return false; } virtual bool supportIPUtils() const { return false; }
virtual IPUtils* iputils() { return nullptr; } virtual IPUtils* iputils() { return nullptr; }
virtual bool supportDnsUtils() const { return false; }
virtual DnsUtils* dnsutils() { return nullptr; } virtual DnsUtils* dnsutils() { return nullptr; }
static bool parseStringList(const QJsonObject& obj, const QString& name, static bool parseStringList(const QJsonObject& obj, const QString& name,
+4 -13
View File
@@ -92,17 +92,6 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.debug() << "Command received:" << type; 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") { if (type == "activate") {
InterfaceConfig config; InterfaceConfig config;
if (!Daemon::parseConfig(obj, config)) { if (!Daemon::parseConfig(obj, config)) {
@@ -126,7 +115,8 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
if (type == "status") { if (type == "status") {
QJsonObject obj = Daemon::instance()->getStatus(); QJsonObject obj = Daemon::instance()->getStatus();
obj.insert("type", "status"); obj.insert("type", "status");
write(obj); m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_socket->write("\n");
return; return;
} }
@@ -134,7 +124,8 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
QJsonObject obj; QJsonObject obj;
obj.insert("type", "logs"); obj.insert("type", "logs");
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|")); obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
write(obj); m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_socket->write("\n");
return; return;
} }
+9 -19
View File
@@ -15,24 +15,13 @@
#include "platforms/ios/QtAppDelegate-C-Interface.h" #include "platforms/ios/QtAppDelegate-C-Interface.h"
#endif #endif
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
bool isAnotherInstanceRunning()
{
QLocalSocket socket;
socket.connectToServer("AmneziaVPNInstance");
if (socket.waitForConnected(500)) {
qWarning() << "AmneziaVPN is already running";
return true;
}
return false;
}
#endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
Migrations migrationsManager; Migrations migrationsManager;
migrationsManager.doMigrations(); migrationsManager.doMigrations();
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
AllowSetForegroundWindow(ASFW_ANY); AllowSetForegroundWindow(ASFW_ANY);
#endif #endif
@@ -43,14 +32,15 @@ int main(int argc, char *argv[])
qputenv("ANDROID_OPENSSL_SUFFIX", "_3"); qputenv("ANDROID_OPENSSL_SUFFIX", "_3");
#endif #endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication app(argc, argv);
#else
AmneziaApplication app(argc, argv); AmneziaApplication app(argc, argv);
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) // if (!app.isPrimary()) {
if (isAnotherInstanceRunning()) { // QTimer::singleShot(1000, &app, [&]() { app.quit(); });
QTimer::singleShot(1000, &app, [&]() { app.quit(); }); // return app.exec();
return app.exec(); // }
}
app.startLocalServer();
#endif #endif
// Allow to raise app window if secondary instance launched // Allow to raise app window if secondary instance launched
+2 -2
View File
@@ -34,8 +34,8 @@ LocalSocketController::LocalSocketController() {
m_socket = new QLocalSocket(this); m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::connected, this, connect(m_socket, &QLocalSocket::connected, this,
&LocalSocketController::daemonConnected); &LocalSocketController::daemonConnected);
connect(m_socket, &QLocalSocket::disconnected, this, connect(m_socket, &QLocalSocket::disconnected, this,
[&] { errorOccurred(QLocalSocket::PeerClosedError); }); &LocalSocketController::disconnected);
connect(m_socket, &QLocalSocket::errorOccurred, this, connect(m_socket, &QLocalSocket::errorOccurred, this,
&LocalSocketController::errorOccurred); &LocalSocketController::errorOccurred);
connect(m_socket, &QLocalSocket::readyRead, this, connect(m_socket, &QLocalSocket::readyRead, this,
+1 -1
View File
@@ -15,7 +15,7 @@ struct WGConfig: Decodable {
let serverPublicKey: String let serverPublicKey: String
let presharedKey: String? let presharedKey: String?
var allowedIPs: [String] var allowedIPs: [String]
var persistentKeepAlive: String var persistentKeepAlive: Int
let splitTunnelType: Int let splitTunnelType: Int
let splitTunnelSites: [String] let splitTunnelSites: [String]
-14
View File
@@ -499,20 +499,6 @@ bool IosController::setupWireGuard()
wgConfig.insert(config_key::persistent_keep_alive, "25"); wgConfig.insert(config_key::persistent_keep_alive, "25");
} }
if (config.contains(config_key::isObfuscationEnabled) && config.value(config_key::isObfuscationEnabled).toBool()) {
wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]);
wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]);
wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]);
wgConfig.insert(config_key::transportPacketMagicHeader, config[config_key::transportPacketMagicHeader]);
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
}
QJsonDocument wgConfigDoc(wgConfig); QJsonDocument wgConfigDoc(wgConfig);
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
@@ -22,6 +22,7 @@ class LinuxDaemon final : public Daemon {
protected: protected:
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; } bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; } IPUtils* iputils() override { return m_iputils; }
@@ -21,6 +21,7 @@ class MacOSDaemon final : public Daemon {
protected: protected:
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; } bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; } IPUtils* iputils() override { return m_iputils; }
@@ -26,6 +26,7 @@ class WindowsDaemon final : public Daemon {
protected: protected:
bool run(Op op, const InterfaceConfig& config) override; bool run(Op op, const InterfaceConfig& config) override;
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
private: private:
@@ -502,7 +502,7 @@ QString WindowsSplitTunnel::convertPath(const QString& path) {
// device should contain : for e.g C: // device should contain : for e.g C:
return ""; return "";
} }
QByteArray buffer(2048, 0xFFu); QByteArray buffer(2048, 0xFF);
auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter), auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter),
(wchar_t*)buffer.data(), buffer.size() / 2); (wchar_t*)buffer.data(), buffer.size() / 2);
@@ -248,7 +248,7 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
} }
if (result != NO_ERROR) { if (result != NO_ERROR) {
logger.error() << "Failed to create route to" logger.error() << "Failed to create route to"
<< prefix.toString() << logger.sensitive(prefix.toString())
<< "result:" << result; << "result:" << result;
} }
return result == NO_ERROR; return result == NO_ERROR;
@@ -265,7 +265,7 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
} }
if (result != NO_ERROR) { if (result != NO_ERROR) {
logger.error() << "Failed to delete route to" logger.error() << "Failed to delete route to"
<< prefix.toString() << logger.sensitive(prefix.toString())
<< "result:" << result; << "result:" << result;
} }
return result == NO_ERROR; return result == NO_ERROR;
+1 -1
View File
@@ -21,7 +21,7 @@
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
constexpr const char* VPN_NAME = "AmneziaVPN"; constexpr const char* VPN_NAME = "AmneziaVPN";
constexpr const char* WIREGUARD_DIR = "AmneziaWG"; constexpr const char* WIREGUARD_DIR = "WireGuard";
constexpr const char* DATA_DIR = "Data"; constexpr const char* DATA_DIR = "Data";
namespace { namespace {
+3 -5
View File
@@ -6,7 +6,6 @@
#include <QTcpSocket> #include <QTcpSocket>
#include <QNetworkInterface> #include <QNetworkInterface>
#include "core/networkUtilities.h"
#include "logger.h" #include "logger.h"
#include "openvpnprotocol.h" #include "openvpnprotocol.h"
#include "utilities.h" #include "utilities.h"
@@ -128,6 +127,7 @@ void OpenVpnProtocol::sendManagementCommand(const QString &command)
uint OpenVpnProtocol::selectMgmtPort() uint OpenVpnProtocol::selectMgmtPort()
{ {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
quint32 port = QRandomGenerator::global()->generate(); quint32 port = QRandomGenerator::global()->generate();
port = (double)(65000 - 15001) * port / UINT32_MAX + 15001; port = (double)(65000 - 15001) * port / UINT32_MAX + 15001;
@@ -137,6 +137,7 @@ uint OpenVpnProtocol::selectMgmtPort()
if (ok) if (ok)
return port; return port;
} }
return m_managementPort; return m_managementPort;
} }
@@ -342,8 +343,7 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
} }
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway); m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer", m_configData.insert("vpnServer", m_configData.value(amnezia::config_key::hostName).toString());
NetworkUtilities::getIPAddress(m_configData.value(amnezia::config_key::hostName).toString()));
IpcClient::Interface()->enablePeerTraffic(m_configData); IpcClient::Interface()->enablePeerTraffic(m_configData);
} }
} }
@@ -352,8 +352,6 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// killSwitch toggle // killSwitch toggle
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
m_configData.insert("vpnServer",
NetworkUtilities::getIPAddress(m_configData.value(amnezia::config_key::hostName).toString()));
IpcClient::Interface()->enableKillSwitch(m_configData, 0); IpcClient::Interface()->enableKillSwitch(m_configData, 0);
} }
#endif #endif
-1
View File
@@ -65,7 +65,6 @@ namespace amnezia
constexpr char last_config[] = "last_config"; constexpr char last_config[] = "last_config";
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig"; constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
constexpr char isObfuscationEnabled[] = "isObfuscationEnabled";
constexpr char junkPacketCount[] = "Jc"; constexpr char junkPacketCount[] = "Jc";
constexpr char junkPacketMinSize[] = "Jmin"; constexpr char junkPacketMinSize[] = "Jmin";
+98 -63
View File
@@ -17,7 +17,6 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent):
m_routeGateway = NetworkUtilities::getGatewayAndIface(); m_routeGateway = NetworkUtilities::getGatewayAndIface();
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr; m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr; m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
m_t2sProcess = IpcClient::InterfaceTun2Socks();
} }
XrayProtocol::~XrayProtocol() XrayProtocol::~XrayProtocol()
@@ -66,7 +65,7 @@ ErrorCode XrayProtocol::start()
}); });
connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus; qDebug().noquote() << "XrayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(Vpn::ConnectionState::Disconnected); setConnectionState(Vpn::ConnectionState::Disconnected);
if (exitStatus != QProcess::NormalExit) { if (exitStatus != QProcess::NormalExit) {
emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed); emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
@@ -92,80 +91,116 @@ ErrorCode XrayProtocol::start()
ErrorCode XrayProtocol::startTun2Sock() ErrorCode XrayProtocol::startTun2Sock()
{ {
m_t2sProcess->start(); if (!QFileInfo::exists(Utils::tun2socksPath())) {
setLastError(ErrorCode::Tun2SockExecutableMissing);
return lastError();
}
m_t2sProcess = IpcClient::CreatePrivilegedProcess();
if (!m_t2sProcess) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_t2sProcess->waitForSource(1000);
if (!m_t2sProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
QString XrayConStr = "socks5://127.0.0.1:" + QString::number(m_localPort);
m_t2sProcess->setProgram(PermittedProcess::Tun2Socks);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
m_configData.insert("inetAdapterIndex", NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress))); m_configData.insert("inetAdapterIndex", NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress)));
#endif QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr, "-tun-post-up",
QString("cmd /c netsh interface ip set address name=\"tun2\" static %1 255.255.255.255").arg(amnezia::protocols::xray::defaultLocalAddr)});
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this,
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this,
[&](int vpnState) {
qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
if (vpnState == Vpn::ConnectionState::Connected)
{
setConnectionState(Vpn::ConnectionState::Connecting);
QList<QHostAddress> dnsAddr;
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
#ifdef Q_OS_WIN
QThread::msleep(8000);
#endif
#ifdef Q_OS_MACOS
QThread::msleep(5000);
IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
#endif #endif
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
QThread::msleep(1000); QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr});
IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr); #endif
IpcClient::Interface()->updateResolvers("tun2", dnsAddr); #ifdef Q_OS_MAC
QStringList arguments({"-device", "utun22", "-proxy", XrayConStr});
#endif
m_t2sProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_t2sProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_t2sProcess.data(), &PrivilegedProcess::stateChanged,
[&](QProcess::ProcessState newState) {
qDebug() << "PrivilegedProcess stateChanged" << newState;
if (newState == QProcess::Running)
{
setConnectionState(Vpn::ConnectionState::Connecting);
QList<QHostAddress> dnsAddr;
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
#ifdef Q_OS_MACOS
QThread::msleep(5000);
IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
#endif
#ifdef Q_OS_WINDOWS
QThread::msleep(7000);
#endif
#ifdef Q_OS_LINUX
QThread::msleep(1000);
IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
#endif #endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// killSwitch toggle // killSwitch toggle
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
m_configData.insert("vpnServer", m_remoteAddress); IpcClient::Interface()->enableKillSwitch(m_configData, 0);
IpcClient::Interface()->enableKillSwitch(m_configData, 0); }
}
#endif #endif
if (m_routeMode == 0) { if (m_routeMode == 0) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress); IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
} }
IpcClient::Interface()->StopRoutingIpv6(); IpcClient::Interface()->StopRoutingIpv6();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
IpcClient::Interface()->updateResolvers("tun2", dnsAddr); IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces(); QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
for (int i = 0; i < netInterfaces.size(); i++) { for (int i = 0; i < netInterfaces.size(); i++) {
for (int j = 0; j < netInterfaces.at(i).addressEntries().size(); j++) for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{ {
// killSwitch toggle // killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index()); IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
}
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer", m_remoteAddress);
IpcClient::Interface()->enablePeerTraffic(m_configData);
}
} }
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer", m_remoteAddress);
IpcClient::Interface()->enablePeerTraffic(m_configData);
} }
#endif
setConnectionState(Vpn::ConnectionState::Connected);
} }
}
#endif
setConnectionState(Vpn::ConnectionState::Connected);
}
});
#if !defined(Q_OS_MACOS) #if !defined(Q_OS_MACOS)
if (vpnState == Vpn::ConnectionState::Disconnected) { connect(m_t2sProcess.data(), &PrivilegedProcess::finished, this,
setConnectionState(Vpn::ConnectionState::Disconnected); [&]() {
IpcClient::Interface()->deleteTun("tun2"); setConnectionState(Vpn::ConnectionState::Disconnected);
IpcClient::Interface()->StartRoutingIpv6(); IpcClient::Interface()->deleteTun("tun2");
IpcClient::Interface()->clearSavedRoutes(); IpcClient::Interface()->StartRoutingIpv6();
} IpcClient::Interface()->clearSavedRoutes();
});
#endif #endif
});
m_t2sProcess->start();
return ErrorCode::NoError; return ErrorCode::NoError;
} }
@@ -179,7 +214,7 @@ void XrayProtocol::stop()
qDebug() << "XrayProtocol::stop()"; qDebug() << "XrayProtocol::stop()";
m_xrayProcess.terminate(); m_xrayProcess.terminate();
if (m_t2sProcess) { if (m_t2sProcess) {
m_t2sProcess->stop(); m_t2sProcess->close();
} }
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
+1 -2
View File
@@ -34,10 +34,9 @@ private:
QString m_secondaryDNS; QString m_secondaryDNS;
#ifndef Q_OS_IOS #ifndef Q_OS_IOS
QProcess m_xrayProcess; QProcess m_xrayProcess;
QSharedPointer<IpcProcessTun2SocksReplica> m_t2sProcess; QSharedPointer<PrivilegedProcess> m_t2sProcess;
#endif #endif
QTemporaryFile m_xrayCfgFile; QTemporaryFile m_xrayCfgFile;
}; };
#endif // XRAYPROTOCOL_H #endif // XRAYPROTOCOL_H
+1 -2
View File
@@ -1,3 +1,2 @@
echo $LC_MESSAGES | grep -qE "en_US.UTF-8|C.UTF-8" || export LC_MESSAGES=C.UTF-8;\
CUR_USER=$(whoami);\ CUR_USER=$(whoami);\
groups $CUR_USER | grep sudo && sudo -nu $CUR_USER sudo -n uname > /dev/null groups $CUR_USER
+1 -1
View File
@@ -237,7 +237,7 @@ private:
mutable SecureQSettings m_settings; mutable SecureQSettings m_settings;
QString m_gatewayEndpoint; QString m_gatewayEndpoint;
bool m_isDevGatewayEnv = false; bool m_isDevGatewayEnv;
}; };
#endif // SETTINGS_H #endif // SETTINGS_H
+7 -10
View File
@@ -34,13 +34,13 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
void ConnectionController::openConnection() void ConnectionController::openConnection()
{ {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) // #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) // if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
{ // {
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning); // emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
return; // return;
} // }
#endif // #endif
int serverIndex = m_serversModel->getDefaultServerIndex(); int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex); QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
@@ -51,9 +51,6 @@ void ConnectionController::openConnection()
if (configVersion == ApiConfigSources::Telegram if (configVersion == ApiConfigSources::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromTelegram(); emit updateApiConfigFromTelegram();
} else if (configVersion == ApiConfigSources::AmneziaGateway
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromGateway();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) { } else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by end_date event"; qDebug() << "attempt to update api config by end_date event";
if (configVersion == ApiConfigSources::Telegram) { if (configVersion == ApiConfigSources::Telegram) {
+17 -20
View File
@@ -39,12 +39,11 @@ namespace
const QString amneziaConfigPatternUserName = "userName"; const QString amneziaConfigPatternUserName = "userName";
const QString amneziaConfigPatternPassword = "password"; const QString amneziaConfigPatternPassword = "password";
const QString amneziaFreeConfigPattern = "api_key"; const QString amneziaFreeConfigPattern = "api_key";
const QString amneziaPremiumConfigPattern = "auth_data";
const QString backupPattern = "Servers/serversList"; const QString backupPattern = "Servers/serversList";
if (config.contains(backupPattern)) { if (config.contains(backupPattern)) {
return ConfigTypes::Backup; return ConfigTypes::Backup;
} else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern) || config.contains(amneziaPremiumConfigPattern) } else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern)
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName) || (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) { && config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia; return ConfigTypes::Amnezia;
@@ -85,7 +84,7 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
return extractConfigFromData(data); return extractConfigFromData(data);
} }
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false); emit importErrorOccurred(tr("Unable to open file"), false);
return false; return false;
} }
@@ -189,12 +188,12 @@ bool ImportController::extractConfigFromData(QString data)
if (!m_serversModel->getServersCount()) { if (!m_serversModel->getServersCount()) {
emit restoreAppConfig(config.toUtf8()); emit restoreAppConfig(config.toUtf8());
} else { } else {
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); emit importErrorOccurred(tr("Invalid configuration file"), false);
} }
break; break;
} }
case ConfigTypes::Invalid: { case ConfigTypes::Invalid: {
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); emit importErrorOccurred(tr("Invalid configuration file"), false);
break; break;
} }
} }
@@ -243,26 +242,24 @@ void ImportController::processNativeWireGuardConfig()
auto containers = m_config.value(config_key::containers).toArray(); auto containers = m_config.value(config_key::containers).toArray();
if (!containers.isEmpty()) { if (!containers.isEmpty()) {
auto container = containers.at(0).toObject(); auto container = containers.at(0).toObject();
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject(); auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject();
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object(); auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5)); QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
QString junkPacketMinSize = QString::number(10); QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50); QString junkPacketMaxSize = QString::number(50);
clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount; protocolConfig[config_key::junkPacketCount] = junkPacketCount;
clientProtocolConfig[config_key::junkPacketMinSize] = junkPacketMinSize; protocolConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
clientProtocolConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize; protocolConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
clientProtocolConfig[config_key::initPacketJunkSize] = "0"; protocolConfig[config_key::initPacketJunkSize] = "0";
clientProtocolConfig[config_key::responsePacketJunkSize] = "0"; protocolConfig[config_key::responsePacketJunkSize] = "0";
clientProtocolConfig[config_key::initPacketMagicHeader] = "1"; protocolConfig[config_key::initPacketMagicHeader] = "1";
clientProtocolConfig[config_key::responsePacketMagicHeader] = "2"; protocolConfig[config_key::responsePacketMagicHeader] = "2";
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3"; protocolConfig[config_key::underloadPacketMagicHeader] = "3";
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4"; protocolConfig[config_key::transportPacketMagicHeader] = "4";
clientProtocolConfig[config_key::isObfuscationEnabled] = true; containerConfig[config_key::last_config] = QString(QJsonDocument(protocolConfig).toJson());
container["wireguard"] = containerConfig;
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
container["wireguard"] = serverProtocolConfig;
containers.replace(0, container); containers.replace(0, container);
m_config[config_key::containers] = containers; m_config[config_key::containers] = containers;
} }
+1
View File
@@ -54,6 +54,7 @@ public slots:
signals: signals:
void importFinished(); void importFinished();
void importErrorOccurred(const QString &errorMessage, bool goToPageHome);
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome); void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void qrDecodingFinished(); void qrDecodingFinished();
+35 -13
View File
@@ -32,8 +32,32 @@ namespace
constexpr char availableCountries[] = "available_countries"; constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config"; constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
} }
#ifdef Q_OS_WINDOWS
QString getNextDriverLetter()
{
QProcess drivesProc;
drivesProc.start("wmic logicaldisk get caption");
drivesProc.waitForFinished();
QString drives = drivesProc.readAll();
qDebug() << drives;
QString letters = "CFGHIJKLMNOPQRSTUVWXYZ";
QString letter;
for (int i = letters.size() - 1; i > 0; i--) {
letter = letters.at(i);
if (!drives.contains(letter + ":"))
break;
}
if (letter == "C:") {
// set err info
qDebug() << "Can't find free drive letter";
return "";
}
return letter;
}
#endif
} }
InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel, InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
@@ -111,10 +135,10 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader; containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
} else if (container == DockerContainer::Sftp) { } else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName); containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(16)); containerConfig.insert(config_key::password, Utils::getRandomString(10));
} else if (container == DockerContainer::Socks5Proxy) { } else if (container == DockerContainer::Socks5Proxy) {
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName); containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(16)); containerConfig.insert(config_key::password, Utils::getRandomString(10));
} }
config.insert(config_key::container, ContainerProps::containerToString(container)); config.insert(config_key::container, ContainerProps::containerToString(container));
@@ -643,7 +667,7 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
QString hostname = serverCredentials.hostName; QString hostname = serverCredentials.hostName;
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
mountPath = Utils::getNextDriverLetter() + ":"; mountPath = getNextDriverLetter() + ":";
// QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3") // QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3")
// .arg(labelTftpUserNameText()) // .arg(labelTftpUserNameText())
// .arg(labelTftpPortText()) // .arg(labelTftpPortText())
@@ -744,7 +768,7 @@ bool InstallController::checkSshConnection(QSharedPointer<ServerController> serv
} else { } else {
if (output.contains(tr("Please login as the user"))) { if (output.contains(tr("Please login as the user"))) {
output.replace("\n", ""); output.replace("\n", "");
emit wrongInstallationUser(output); emit installationErrorOccurred(output);
return false; return false;
} }
} }
@@ -802,7 +826,7 @@ bool InstallController::installServiceFromApi()
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(), m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig); m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode); emit installationErrorOccurred(errorCode);
return false; return false;
@@ -829,26 +853,24 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
QJsonObject newServerConfig; QJsonObject newServerConfig;
ErrorCode errorCode = apiController.getConfigForService( ErrorCode errorCode =
m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), apiController.getConfigForService(m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(),
apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, apiConfig.value(configKey::serviceType).toString(),
authData, newServerConfig); apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, newServerConfig);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode); emit installationErrorOccurred(errorCode);
return false; return false;
} }
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); 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::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType)); newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol)); newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, authData);
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
m_serversModel->editServer(newServerConfig, serverIndex); m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) { if (reloadServiceConfig) {
+1 -1
View File
@@ -75,8 +75,8 @@ signals:
void removeAllContainersFinished(const QString &finishedMessage); void removeAllContainersFinished(const QString &finishedMessage);
void removeProcessedContainerFinished(const QString &finishedMessage); void removeProcessedContainerFinished(const QString &finishedMessage);
void installationErrorOccurred(const QString &errorMessage);
void installationErrorOccurred(ErrorCode errorCode); void installationErrorOccurred(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverAlreadyExists(int serverIndex); void serverAlreadyExists(int serverIndex);
-4
View File
@@ -39,9 +39,6 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
case CountryNameRole: { case CountryNameRole: {
return countryInfo.value(configKey::serverCountryName).toString(); return countryInfo.value(configKey::serverCountryName).toString();
} }
case CountryImageCodeRole: {
return countryInfo.value(configKey::serverCountryCode).toString().toUpper();
}
} }
return QVariant(); return QVariant();
@@ -79,6 +76,5 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[CountryNameRole] = "countryName"; roles[CountryNameRole] = "countryName";
roles[CountryCodeRole] = "countryCode"; roles[CountryCodeRole] = "countryCode";
roles[CountryImageCodeRole] = "countryImageCode";
return roles; return roles;
} }
+1 -2
View File
@@ -11,8 +11,7 @@ class ApiCountryModel : public QAbstractListModel
public: public:
enum Roles { enum Roles {
CountryNameRole = Qt::UserRole + 1, CountryNameRole = Qt::UserRole + 1,
CountryCodeRole, CountryCodeRole
CountryImageCodeRole
}; };
explicit ApiCountryModel(QObject *parent = nullptr); explicit ApiCountryModel(QObject *parent = nullptr);
+1 -3
View File
@@ -77,7 +77,6 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
{ {
beginResetModel(); beginResetModel();
m_clientsTable = QJsonArray(); m_clientsTable = QJsonArray();
endResetModel();
ErrorCode error = ErrorCode::NoError; ErrorCode error = ErrorCode::NoError;
@@ -91,10 +90,10 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error); const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server"; logger.error() << "Failed to get the clientsTable file from the server";
endResetModel();
return error; return error;
} }
beginResetModel();
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) { if (m_clientsTable.isEmpty()) {
@@ -602,6 +601,5 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
roles[LatestHandshakeRole] = "latestHandshake"; roles[LatestHandshakeRole] = "latestHandshake";
roles[DataReceivedRole] = "dataReceived"; roles[DataReceivedRole] = "dataReceived";
roles[DataSentRole] = "dataSent"; roles[DataSentRole] = "dataSent";
roles[AllowedIpsRole] = "allowedIps";
return roles; return roles;
} }
+1 -1
View File
@@ -771,5 +771,5 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
if (countryCode.isEmpty()) { if (countryCode.isEmpty()) {
return ""; return "";
} }
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode);
} }
@@ -90,7 +90,7 @@ PageType {
Layout.rightMargin: 32 Layout.rightMargin: 32
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg"
} }
} }
@@ -49,8 +49,6 @@ PageType {
HeaderType { HeaderType {
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
@@ -58,7 +56,7 @@ PageType {
headerText: qsTr("Connection") headerText: qsTr("Connection")
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : "" actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : ""
actionButtonFunction: function() { actionButtonFunction: function() {
moreActionsDrawer.open() moreActionsDrawer.open()
} }
@@ -69,19 +67,18 @@ PageType {
parent: root parent: root
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height * 0.5 expandedHeight: root.height * 0.35
expandedContent: ColumnLayout { expandedContent: ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 0 anchors.leftMargin: 16
anchors.rightMargin: 16
HeaderType { HeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Settings") headerText: qsTr("Settings")
} }
@@ -90,12 +87,9 @@ PageType {
id: switcher id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable logs") text: qsTr("Enable logs")
visible: PageController.isStartPageVisible()
checked: SettingsController.isLoggingEnabled checked: SettingsController.isLoggingEnabled
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) { if (checked !== SettingsController.isLoggingEnabled) {
@@ -104,28 +98,6 @@ PageType {
} }
} }
LabelWithButtonType {
id: supportUuid
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Support tag")
descriptionText: SettingsController.getInstallationUuid()
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
visible: SettingsController.getInstallationUuid() !== ""
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
} }
} }
} }
@@ -37,7 +37,7 @@ PageType {
Connections { Connections {
target: ImportController target: ImportController
function onImportErrorOccurred(error, goToPageHome) { function onImportErrorOccurred(errorMessage, goToPageHome) {
if (goToPageHome) { if (goToPageHome) {
PageController.goToStartPage() PageController.goToStartPage()
} else { } else {
+43 -40
View File
@@ -772,8 +772,7 @@ PageType {
} }
} }
width: root.width anchors.fill: parent
height: root.height
expandedContent: ColumnLayout { expandedContent: ColumnLayout {
id: expandedContent id: expandedContent
@@ -784,6 +783,8 @@ PageType {
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.rightMargin: 16 anchors.rightMargin: 16
spacing: 8
onImplicitHeightChanged: { onImplicitHeightChanged: {
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32 clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
} }
@@ -796,54 +797,57 @@ PageType {
} }
} }
Header2TextType { Header2Type {
Layout.maximumWidth: parent.width Layout.fillWidth: true
headerText: clientName
}
ColumnLayout
{
id: textColumn
property string textColor: AmneziaStyle.color.mutedGray
Layout.bottomMargin: 24 Layout.bottomMargin: 24
text: clientName ParagraphTextType {
maximumLineCount: 2 color: textColumn.textColor
wrapMode: Text.Wrap visible: creationDate
elide: Qt.ElideRight Layout.fillWidth: true
}
ParagraphTextType { text: qsTr("Creation date: %1").arg(creationDate)
color: AmneziaStyle.color.mutedGray }
visible: creationDate
Layout.fillWidth: true
text: qsTr("Creation date: %1").arg(creationDate) ParagraphTextType {
} color: textColumn.textColor
visible: latestHandshake
Layout.fillWidth: true
ParagraphTextType { text: qsTr("Latest handshake: %1").arg(latestHandshake)
color: AmneziaStyle.color.mutedGray }
visible: latestHandshake
Layout.fillWidth: true
text: qsTr("Latest handshake: %1").arg(latestHandshake) ParagraphTextType {
} color: textColumn.textColor
visible: dataReceived
Layout.fillWidth: true
ParagraphTextType { text: qsTr("Data received: %1").arg(dataReceived)
color: AmneziaStyle.color.mutedGray }
visible: dataReceived
Layout.fillWidth: true
text: qsTr("Data received: %1").arg(dataReceived) ParagraphTextType {
} color: textColumn.textColor
visible: dataSent
Layout.fillWidth: true
ParagraphTextType { text: qsTr("Data sent: %1").arg(dataSent)
color: AmneziaStyle.color.mutedGray }
visible: dataSent
Layout.fillWidth: true
text: qsTr("Data sent: %1").arg(dataSent) ParagraphTextType {
} color: textColumn.textColor
visible: allowedIps
Layout.fillWidth: true
ParagraphTextType { text: qsTr("Allowed IPs: %1").arg(allowedIps)
color: AmneziaStyle.color.mutedGray }
visible: allowedIps
Layout.fillWidth: true
text: qsTr("Allowed IPs: %1").arg(allowedIps)
} }
Item { Item {
@@ -948,7 +952,6 @@ PageType {
BasicButtonType { BasicButtonType {
id: revokeButton id: revokeButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
-4
View File
@@ -123,10 +123,6 @@ PageType {
} }
} }
function onWrongInstallationUser(message) {
onInstallationErrorOccurred(message)
}
function onUpdateContainerFinished(message) { function onUpdateContainerFinished(message) {
PageController.showNotificationMessage(message) PageController.showNotificationMessage(message)
PageController.closePage() PageController.closePage()
+1 -2
View File
@@ -80,8 +80,7 @@ Window {
} }
PageStart { PageStart {
width: root.width anchors.fill: parent
height: root.height
} }
Item { Item {
Executable → Regular
+30 -139
View File
@@ -10,72 +10,18 @@
#include <QJsonObject> #include <QJsonObject>
#include "utilities.h" #include "utilities.h"
#include "version.h"
#ifdef Q_OS_WINDOWS
QString printErrorMessage(DWORD errorCode) {
LPVOID lpMsgBuf;
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
FormatMessageW(
dwFlags,
NULL,
errorCode,
dwLanguageId,
(LPWSTR)&lpMsgBuf,
0,
NULL
);
QString errorMsg = QString::fromWCharArray((LPCWSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
return errorMsg.trimmed();
}
QString Utils::getNextDriverLetter()
{
DWORD drivesBitmask = GetLogicalDrives();
if (drivesBitmask == 0) {
DWORD error = GetLastError();
qDebug() << "GetLogicalDrives failed. Error code:" << error;
return "";
}
QString letters = "FGHIJKLMNOPQRSTUVWXYZ";
QString availableLetter;
for (int i = letters.size() - 1; i >= 0; --i) {
QChar letterChar = letters.at(i);
int driveIndex = letterChar.toLatin1() - 'A';
if ((drivesBitmask & (1 << driveIndex)) == 0) {
availableLetter = letterChar;
break;
}
}
if (availableLetter.isEmpty()) {
qDebug() << "Can't find free drive letter";
return "";
}
return availableLetter;
}
#endif
QString Utils::getRandomString(int len) QString Utils::getRandomString(int len)
{ {
const QString possibleCharacters = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString; QString randomString;
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
randomString.append(possibleCharacters.at(QRandomGenerator::system()->bounded(possibleCharacters.length()))); quint32 index = QRandomGenerator::global()->generate() % possibleCharacters.length();
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
} }
return randomString; return randomString;
} }
@@ -163,34 +109,30 @@ QString Utils::usrExecutable(const QString &baseName)
bool Utils::processIsRunning(const QString &fileName, const bool fullFlag) bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); QProcess process;
if (hSnapshot == INVALID_HANDLE_VALUE) { process.setReadChannel(QProcess::StandardOutput);
qWarning() << "Utils::processIsRunning error CreateToolhelp32Snapshot"; process.setProcessChannelMode(QProcess::MergedChannels);
return false; process.start("wmic.exe",
} QStringList() << "/OUTPUT:STDOUT"
<< "PROCESS"
<< "get"
<< "Caption");
process.waitForStarted();
process.waitForFinished();
QString processData(process.readAll());
QStringList processList = processData.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts);
foreach (const QString &rawLine, processList) {
const QString line = rawLine.simplified();
if (line.isEmpty()) {
continue;
}
PROCESSENTRY32W pe32; if (line == fileName) {
pe32.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32FirstW(hSnapshot, &pe32)) {
CloseHandle(hSnapshot);
qWarning() << "Utils::processIsRunning error Process32FirstW";
return false;
}
do {
QString exeFile = QString::fromWCharArray(pe32.szExeFile);
if (exeFile.compare(fileName, Qt::CaseInsensitive) == 0) {
CloseHandle(hSnapshot);
return true; return true;
} }
} while (Process32NextW(hSnapshot, &pe32)); }
CloseHandle(hSnapshot);
return false; return false;
#elif defined(Q_OS_IOS)
#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
return false; return false;
#else #else
QProcess process; QProcess process;
@@ -208,45 +150,13 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
#endif #endif
} }
bool Utils::killProcessByName(const QString &name) void Utils::killProcessByName(const QString &name)
{ {
qDebug().noquote() << "Kill process" << name; qDebug().noquote() << "Kill process" << name;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); QProcess::execute("taskkill", QStringList() << "/IM" << name << "/F");
if (hSnapshot == INVALID_HANDLE_VALUE) #elif defined Q_OS_IOS
return false; return;
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(PROCESSENTRY32W);
bool success = false;
if (Process32FirstW(hSnapshot, &pe32)) {
do {
QString exeFile = QString::fromWCharArray(pe32.szExeFile);
if (exeFile.compare(name, Qt::CaseInsensitive) == 0) {
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
if (hProcess != NULL) {
if (TerminateProcess(hProcess, 0)) {
success = true;
} else {
DWORD error = GetLastError();
qCritical() << "Can't terminate process" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error);
}
CloseHandle(hProcess);
} else {
DWORD error = GetLastError();
qCritical() << "Can't open process for termination" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error);
}
}
} while (Process32NextW(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return success;
#elif defined Q_OS_IOS || defined(Q_OS_ANDROID)
return false;
#else #else
QProcess::execute(QString("pkill %1").arg(name)); QProcess::execute(QString("pkill %1").arg(name));
#endif #endif
@@ -334,22 +244,3 @@ bool Utils::signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
} }
#endif #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 (...) {}
}
Executable → Regular
+2 -7
View File
@@ -7,8 +7,7 @@
#include <QJsonDocument> #include <QJsonDocument>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include "Windows.h"
#include <tlhelp32.h>
#endif #endif
class Utils : public QObject class Utils : public QObject
@@ -28,19 +27,15 @@ public:
static bool initializePath(const QString &path); static bool initializePath(const QString &path);
static bool processIsRunning(const QString &fileName, const bool fullFlag = false); static bool processIsRunning(const QString &fileName, const bool fullFlag = false);
static bool killProcessByName(const QString &name); static void killProcessByName(const QString &name);
static QString openVpnExecPath(); static QString openVpnExecPath();
static QString wireguardExecPath(); static QString wireguardExecPath();
static QString certUtilPath(); static QString certUtilPath();
static QString tun2socksPath(); 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 #ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent); static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
static QString getNextDriverLetter();
#endif #endif
}; };
+2 -3
View File
@@ -56,15 +56,14 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{ {
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex()); QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
if (IpcClient::Interface()) { if (IpcClient::Interface()) {
if (state == Vpn::ConnectionState::Connected) { if (state == Vpn::ConnectionState::Connected) {
IpcClient::Interface()->resetIpStack(); IpcClient::Interface()->resetIpStack();
IpcClient::Interface()->flushDns(); IpcClient::Interface()->flushDns();
if (!m_vpnConfiguration.value(config_key::configVersion).toInt() && container != DockerContainer::Awg if (!m_vpnConfiguration.value(config_key::configVersion).toInt()) {
&& container != DockerContainer::WireGuard) {
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
+1
View File
@@ -7,6 +7,7 @@
class IpcInterface class IpcInterface
{ {
SLOT( int createPrivilegedProcess() ); // return local pid SLOT( int createPrivilegedProcess() ); // return local pid
//SIGNAL(sendMessage(const QByteArray &message));
// Route functions // Route functions
SLOT( int routeAddList(const QString &gw, const QStringList &ips) ); SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
+1
View File
@@ -3,6 +3,7 @@
class IpcProcessInterface class IpcProcessInterface
{ {
//SLOT( start(const QString &program, const QStringList &args) );
SLOT( start() ); SLOT( start() );
SLOT( close() ); SLOT( close() );
-11
View File
@@ -1,11 +0,0 @@
#include <QtCore>
#include <QString>
class IpcProcessTun2Socks
{
SLOT( start() );
SLOT( stop() );
SIGNAL( setConnectionState(int state) );
SIGNAL( stateChanged(QProcess::ProcessState newState) );
};
+4 -4
View File
@@ -8,7 +8,6 @@
#include "logger.h" #include "logger.h"
#include "router.h" #include "router.h"
#include "../core/networkUtilities.h"
#include "../client/protocols/protocols_defs.h" #include "../client/protocols/protocols_defs.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsdaemon.h" #include "../client/platforms/windows/daemon/windowsdaemon.h"
@@ -210,7 +209,7 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
if (splitTunnelType == 0) { if (splitTunnelType == 0) {
blockAll = true; blockAll = true;
allowNets = true; allowNets = true;
allownets.append(configStr.value("vpnServer").toString()); allownets.append(configStr.value(amnezia::config_key::hostName).toString());
} else if (splitTunnelType == 1) { } else if (splitTunnelType == 1) {
blockNets = true; blockNets = true;
for (auto v : splitTunnelSites) { for (auto v : splitTunnelSites) {
@@ -219,7 +218,7 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
} else if (splitTunnelType == 2) { } else if (splitTunnelType == 2) {
blockAll = true; blockAll = true;
allowNets = true; allowNets = true;
allownets.append(configStr.value("vpnServer").toString()); allownets.append(configStr.value(amnezia::config_key::hostName).toString());
for (auto v : splitTunnelSites) { for (auto v : splitTunnelSites) {
allownets.append(v.toString()); allownets.append(v.toString());
} }
@@ -330,7 +329,7 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
} }
} }
config.m_excludedAddresses.append(configStr.value("vpnServer").toString()); config.m_excludedAddresses.append(configStr.value(amnezia::config_key::hostName).toString());
if (splitTunnelType == 2) { if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) { for (auto v : splitTunnelSites) {
QString ipRange = v.toString(); QString ipRange = v.toString();
@@ -352,6 +351,7 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex); WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
return true;
#endif #endif
return true; return true;
} }
-4
View File
@@ -9,10 +9,8 @@
#include "ipc.h" #include "ipc.h"
#include "ipcserverprocess.h" #include "ipcserverprocess.h"
#include "ipctun2socksprocess.h"
#include "rep_ipc_interface_source.h" #include "rep_ipc_interface_source.h"
#include "rep_ipc_process_tun2socks_source.h"
class IpcServer : public IpcInterfaceSource class IpcServer : public IpcInterfaceSource
{ {
@@ -46,12 +44,10 @@ private:
ProcessDescriptor (QObject *parent = nullptr) { ProcessDescriptor (QObject *parent = nullptr) {
serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent)); serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent));
ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent)); ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent));
tun2socksProcess = QSharedPointer<IpcProcessTun2Socks>(new IpcProcessTun2Socks(parent));
localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent)); localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent));
} }
QSharedPointer<IpcServerProcess> ipcProcess; QSharedPointer<IpcServerProcess> ipcProcess;
QSharedPointer<IpcProcessTun2Socks> tun2socksProcess;
QSharedPointer<QRemoteObjectHost> serverNode; QSharedPointer<QRemoteObjectHost> serverNode;
QSharedPointer<QLocalServer> localServer; QSharedPointer<QLocalServer> localServer;
}; };
-74
View File
@@ -1,74 +0,0 @@
#include "ipctun2socksprocess.h"
#include "ipc.h"
#include <QProcess>
#include <QString>
#include "../protocols/protocols_defs.h"
#ifndef Q_OS_IOS
IpcProcessTun2Socks::IpcProcessTun2Socks(QObject *parent) :
IpcProcessTun2SocksSource(parent),
m_t2sProcess(QSharedPointer<QProcess>(new QProcess()))
{
connect(m_t2sProcess.data(), &QProcess::stateChanged, this, &IpcProcessTun2Socks::stateChanged);
qDebug() << "IpcProcessTun2Socks::IpcProcessTun2Socks()";
}
IpcProcessTun2Socks::~IpcProcessTun2Socks()
{
qDebug() << "IpcProcessTun2Socks::~IpcProcessTun2Socks()";
}
void IpcProcessTun2Socks::start()
{
qDebug() << "IpcProcessTun2Socks::start()";
m_t2sProcess->setProgram(amnezia::permittedProcessPath(static_cast<amnezia::PermittedProcess>(amnezia::PermittedProcess::Tun2Socks)));
QString XrayConStr = "socks5://127.0.0.1:10808";
#ifdef Q_OS_WIN
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr, "-tun-post-up",
QString("cmd /c netsh interface ip set address name=\"tun2\" static %1 255.255.255.255")
.arg(amnezia::protocols::xray::defaultLocalAddr)});
#endif
#ifdef Q_OS_LINUX
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr});
#endif
#ifdef Q_OS_MAC
QStringList arguments({"-device", "utun22", "-proxy", XrayConStr});
#endif
m_t2sProcess->setArguments(arguments);
Utils::killProcessByName(m_t2sProcess->program());
m_t2sProcess->start();
connect(m_t2sProcess.data(), &QProcess::readyReadStandardOutput, this, [this]() {
QString line = m_t2sProcess.data()->readAllStandardOutput();
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://127.0.0.1")) {
emit setConnectionState(Vpn::ConnectionState::Connected);
}
});
connect(m_t2sProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "tun2socks finished, exitCode, exiStatus" << exitCode << exitStatus;
emit setConnectionState(Vpn::ConnectionState::Disconnected);
if (exitStatus != QProcess::NormalExit){
stop();
}
if (exitCode !=0 ){
stop();
}
});
m_t2sProcess->start();
m_t2sProcess->waitForStarted();
}
void IpcProcessTun2Socks::stop()
{
qDebug() << "IpcProcessTun2Socks::stop()";
m_t2sProcess->close();
}
#endif
-52
View File
@@ -1,52 +0,0 @@
#ifndef IPCTUN2SOCKSPROCESS_H
#define IPCTUN2SOCKSPROCESS_H
#include <QObject>
#ifndef Q_OS_IOS
#include "rep_ipc_process_tun2socks_source.h"
namespace Vpn
{
Q_NAMESPACE
enum ConnectionState {
Unknown,
Disconnected,
Preparing,
Connecting,
Connected,
Disconnecting,
Reconnecting,
Error
};
Q_ENUM_NS(ConnectionState)
}
class IpcProcessTun2Socks : public IpcProcessTun2SocksSource
{
Q_OBJECT
public:
explicit IpcProcessTun2Socks(QObject *parent = nullptr);
virtual ~IpcProcessTun2Socks();
void start() override;
void stop() override;
signals:
private:
QSharedPointer<QProcess> m_t2sProcess;
};
#else
class IpcProcessTun2Socks : public QObject
{
Q_OBJECT
public:
explicit IpcProcessTun2Socks(QObject *parent = nullptr);
};
#endif
#endif // IPCTUN2SOCKSPROCESS_H
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

-3
View File
@@ -18,7 +18,6 @@ set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipctun2socksprocess.h
${CMAKE_CURRENT_LIST_DIR}/localserver.h ${CMAKE_CURRENT_LIST_DIR}/localserver.h
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
${CMAKE_CURRENT_LIST_DIR}/router.h ${CMAKE_CURRENT_LIST_DIR}/router.h
@@ -31,7 +30,6 @@ set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipctun2socksprocess.cpp
${CMAKE_CURRENT_LIST_DIR}/localserver.cpp ${CMAKE_CURRENT_LIST_DIR}/localserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp
@@ -281,7 +279,6 @@ endif()
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_interface.rep) qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_interface.rep)
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_process_interface.rep) qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_process_interface.rep)
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_process_tun2socks.rep)
# copy deploy artifacts required to run the application to the debug build folder # copy deploy artifacts required to run the application to the debug build folder
if(WIN32) if(WIN32)
-1
View File
@@ -37,7 +37,6 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
if (!m_isRemotingEnabled) { if (!m_isRemotingEnabled) {
m_isRemotingEnabled = true; m_isRemotingEnabled = true;
m_serverNode.enableRemoting(&m_ipcServer); m_serverNode.enableRemoting(&m_ipcServer);
m_serverNode.enableRemoting(&m_tun2socks);
} }
}); });
-1
View File
@@ -38,7 +38,6 @@ public:
~LocalServer(); ~LocalServer();
QSharedPointer<QLocalServer> m_server; QSharedPointer<QLocalServer> m_server;
IpcServer m_ipcServer; IpcServer m_ipcServer;
IpcProcessTun2Socks m_tun2socks;
QRemoteObjectHost m_serverNode; QRemoteObjectHost m_serverNode;
bool m_isRemotingEnabled = false; bool m_isRemotingEnabled = false;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX