Compare commits

...

94 Commits
1.2 ... 1.5

Author SHA1 Message Date
pokamest
40a5b2e3f3 sites list ui fix 2021-02-25 23:11:46 +03:00
pokamest
c683884868 sites list reimplement 2021-02-25 21:16:00 +03:00
pokamest
65961d8d2e vpnconnection.cpp crash fix
ss server sript fix
2021-02-25 18:05:42 +03:00
pokamest
7dc1f1e225 Utils ip address regexp 2021-02-25 18:03:24 +03:00
pokamest
a47ab15aef mainwindow.ui fix 2021-02-25 01:06:02 +03:00
pokamest
c74efdaa9b ui fix for macos 2021-02-24 13:41:32 -08:00
pokamest
39224c7bf7 SingleApplication 2021-02-24 13:38:23 -08:00
pokamest
96aa3d409d SingleApplication 2021-02-24 23:40:57 +03:00
pokamest
c63990f720 Auto start
Auto connect
Dns settings
ui fixes
2021-02-24 21:58:32 +03:00
pokamest
ad643bf76e new icon 2021-02-22 18:07:39 +03:00
pokamest
c7ea4966fd minor fixes:
-build_windows.bat
-win build fix
-qdebug fix
2021-02-22 16:31:43 +03:00
pokamest
8fd81be477 ShadowSocks fixes for MacOS 2021-02-21 09:44:53 -08:00
pokamest
a1cb4ac544 Custom routing done
ShadowSocks enabled by default
2021-02-18 15:00:41 +03:00
pokamest
f91854594c Merge branch 'dev' into service_refact 2021-02-11 19:17:16 +03:00
pokamest
f661ea1d46 Merge branch 'macos_build_fix' into dev 2021-02-10 10:44:37 -08:00
pokamest
f50eea3eaf macos signing fixes 2021-02-10 06:57:26 -08:00
pokamest
c15b57e690 windows travis fix 2021-02-10 00:07:12 +03:00
pokamest
5f7ef31345 win cert updated 2021-02-09 00:33:26 +03:00
pokamest
447410a27a Macos build fix (#6)
macos deploy fixes
2021-02-08 23:57:35 +03:00
pokamest
2aa9f9cca9 macos build fix 2021-02-08 12:42:48 -08:00
pokamest
cba27d354d macos deploy fixes 2021-02-08 21:10:34 +03:00
pokamest
b398f42ada ipc process fix 2021-02-03 20:05:50 +03:00
pokamest
b6571d99de Qt ro refact 2021-02-03 15:42:36 +03:00
pokamest
b2392c1943 Qt Remote objects done 2021-02-02 22:51:31 +03:00
pokamest
048a673d31 Qt remote objects IPC 2021-02-02 01:47:40 +03:00
pokamest
c4df9c004b Merge branch 'dev' into service_refact 2021-01-30 15:03:01 +03:00
pokamest
b9d4c53eb6 travis macos fix 2021-01-28 14:18:28 +03:00
pokamest
5fdeea0581 travis fix 2021-01-28 13:26:43 +03:00
pokamest
e1cd764050 travis fix 2021-01-28 13:11:10 +03:00
pokamest
1e54379cc0 macos travis fix 2021-01-28 12:54:09 +03:00
pokamest
ba3f69d206 travis fix 2021-01-27 01:44:32 +03:00
pokamest
fcfd4181c7 merge fix 2021-01-27 01:32:36 +03:00
pokamest
9f6ec6d3e2 Merge branch 'master' into gitfix 2021-01-27 00:50:55 +03:00
pokamest
2e50aae603 macos deploy 2021-01-26 22:15:49 +03:00
pokamest
5a16a07b57 travis release 2021-01-26 22:00:58 +03:00
pokamest
953eca6695 Bug fixes:
- auto install tap
- share connectionState
- service crash fix
2021-01-26 15:01:15 +03:00
pokamest
a50cdd5bc2 travis fix 2021-01-23 16:45:11 +03:00
pokamest
10eb65e545 travis fix 2021-01-23 16:28:06 +03:00
pokamest
5354344ba7 travis fix 2021-01-23 16:02:21 +03:00
pokamest
c000a93eef travis fix 2021-01-23 15:13:46 +03:00
pokamest
ef6bc5ae4a travis fix 2021-01-23 15:08:01 +03:00
pokamest
97c4564649 travis fix 2021-01-23 15:03:56 +03:00
pokamest
5bf31a8b50 travis fix 2021-01-23 01:09:27 +03:00
pokamest
18e0d7fb2c travis fix 2021-01-23 00:57:12 +03:00
pokamest
ac454d9d78 Server scripts fix 2021-01-22 16:06:43 +03:00
pokamest
806677510f icon changed 2021-01-21 19:16:10 +03:00
pokamest
68e0ba9923 server scripts fix 2021-01-21 19:14:07 +03:00
pokamest
22b33a4f25 remote_obj 2021-01-20 23:07:23 +03:00
pokamest
77f830acd5 travis fix 2021-01-19 09:48:50 +03:00
pokamest
eb3b090257 travis fix 2021-01-19 01:58:55 +03:00
pokamest
2e1c219093 travis fix 2021-01-19 01:54:42 +03:00
pokamest
e38f69caf0 travis fix 2021-01-19 01:54:27 +03:00
pokamest
795f286923 travis fix 2021-01-19 01:45:47 +03:00
pokamest
d62e5b51da travis fix 2021-01-19 01:44:17 +03:00
pokamest
96b267b1f7 travis fix 2021-01-19 01:31:59 +03:00
pokamest
568388367e travis fix 2021-01-19 01:18:23 +03:00
pokamest
5ca46edf33 travis fix 2021-01-19 01:15:05 +03:00
pokamest
af41abd346 travis fix 2021-01-19 01:09:41 +03:00
pokamest
5edb61641c travis fix 2021-01-19 00:27:35 +03:00
pokamest
af3b1caa87 travis fix 2021-01-18 22:30:32 +03:00
pokamest
cc4a7b32b0 travis fix 2021-01-18 22:07:31 +03:00
pokamest
dfd61f58d4 travis fix 2021-01-18 21:58:23 +03:00
pokamest
84a3c1f725 travis fix 2021-01-18 19:00:18 +03:00
pokamest
2419fa1a6e travis fix 2021-01-18 18:45:38 +03:00
pokamest
3b8ecd9e9d travis windows fix 2021-01-18 01:41:20 +03:00
pokamest
94ee4b76db travis fix 2021-01-18 01:18:22 +03:00
pokamest
d43a1cd753 travis fix 2021-01-18 01:14:04 +03:00
pokamest
8bc6852b39 travis fix 2021-01-17 16:27:33 +03:00
pokamest
3850be08f1 travis fix 2021-01-17 16:22:05 +03:00
pokamest
d72d1d56b1 travis fix 2021-01-17 16:15:52 +03:00
pokamest
2a4926843c travis fix 2021-01-17 14:14:26 +03:00
pokamest
1b357055a2 travis fix 2021-01-17 13:53:05 +03:00
pokamest
2d3d64e462 travis fix 2021-01-17 13:17:37 +03:00
pokamest
aa648d4e12 travis scripts 2021-01-17 13:11:39 +03:00
pokamest
bfa0ac4c34 Macos doc icon 2021-01-16 21:04:16 +03:00
pokamest
c37ddd83d5 ui fix 2021-01-16 15:08:27 +03:00
pokamest
96bbc1cdbc ss fix 2021-01-15 23:37:34 +03:00
pokamest
ec9ae0ef4f shadowsocks impl 2021-01-15 23:36:35 +03:00
pokamest
95cbb07cbb Merge branch 'dev' into ss/impl 2021-01-12 12:57:58 +03:00
pokamest
7b95d6a5fc ss files 2021-01-11 23:19:30 +03:00
driftingsun
bea19a9461 Fix issue with easyrsa 2021-01-11 16:41:17 +03:00
pokamest
ba686556d2 Merge branch 'dev' into ss/impl 2021-01-10 20:52:04 +03:00
pokamest
15dc6eaaf8 ss files 2021-01-10 20:37:57 +03:00
driftingsun
3369a387ce Fix crash on fatal error, remove moc files from Windows installation 2021-01-10 18:47:29 +03:00
michael
dd46d8cf99 Fix some warnings, fix installation scripts (macOS) 2021-01-10 16:50:38 +03:00
pokamest
0b8c8835c4 new server page fix 2021-01-09 20:05:16 +03:00
pokamest
441811e277 Refactoring
Ui improvements
Bug fixes
2021-01-09 19:55:16 +03:00
pokamest
3645735053 easyrsa scripts for MacOS 2021-01-08 18:10:52 +03:00
pokamest
416d597847 MacOS deploy script fix 2021-01-08 18:08:20 +03:00
pokamest
861c71e3a8 OpenVpn runtime error codes handling 2021-01-08 16:51:58 +03:00
pokamest
f45fb442de Scripts fix for macos 2021-01-08 15:43:45 +03:00
pokamest
82241701a9 deploy fix 2021-01-08 00:34:15 +03:00
pokamest
bfdbe27a8d Openvpn scripts fixes
some refactoring
2021-01-07 20:53:42 +03:00
pokamest
c7dafe9c00 Crash fix in management server 2021-01-07 19:10:24 +03:00
85 changed files with 3591 additions and 1814 deletions

1
.gitignore vendored
View File

@@ -35,6 +35,7 @@ CMakeLists.txt.user*
.DS_Store
._.DS_Store
._*
*.dmg
# tmp files
*.*~

View File

@@ -11,16 +11,30 @@ jobs:
- name: MacOS
os: osx
osx_image: xcode12.2
env:
- QT_VERSION=5.15.1
script:
- |
if [ ! -f $HOME/Qt/5.14.2/clang_64/bin/qmake ]; then \
if [ ! -f $HOME/Qt/$QT_VERSION/clang_64/bin/qmake ]; then \
brew install p7zip && \
pip3 install aqtinstall requests py7zr && \
python3 -m aqt install --outputdir $HOME/Qt 5.14.2 mac desktop clang_64 -m qtbase && \
python3 -m aqt install --outputdir $HOME/Qt $QT_VERSION mac desktop clang_64 -m qtbase && \
python3 -m aqt tool --outputdir $HOME/Qt mac tools_ifw 4.0.1 qt.tools.ifw.40;
fi
- bash deploy/build_macos.sh
- bash deploy/build_macos.sh
deploy:
provider: releases
token: $GH_TOKEN
skip_cleanup: true
file:
- "AmneziaVPN_unsigned.dmg"
on:
tags: true
branch: master
- name: Windows
os: windows
@@ -44,24 +58,24 @@ jobs:
- echo -e "\r\n" >> winbuild.bat
- echo 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsamd64_x86.bat"' >> winbuild.bat
- echo -e "\r\n" >> winbuild.bat
- echo -e "deploy\\\build_windows.bat" >> winbuild.bat
- cat winbuild.bat
- echo -e "set WIN_CERT_PW=$WIN_CERT_PW" >> winbuild.bat
- echo -e "\r\n" >> winbuild.bat
- echo -e "call deploy\\\build_windows.bat" >> winbuild.bat
- cmd //c winbuild.bat
deploy:
provider: releases
token: $GH_TOKEN
skip_cleanup: true
file:
- "AmneziaVPN.exe"
on:
tags: true
branch: master
deploy:
provider: releases
api_key: $GH_TOKEN
file:
- "AmneziaVPN.exe"
- "AmneziaVPN.dmg"
skip_cleanup: true
on:
tags: true
branch: master
before_cache:
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then brew cleanup; fi
# Cache only .git files under "/usr/local/Homebrew" so "brew update" does not take 5min every build
@@ -71,4 +85,4 @@ cache:
directories:
- $HOME/Qt
- /C/Qt
- $HOME/Library/Caches/Homebrew
- $HOME/Library/Caches/Homebrew

View File

@@ -1,2 +1,3 @@
TEMPLATE = subdirs
SUBDIRS = client service platform

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
QT += widgets core gui network xml
QT += widgets core gui network xml remoteobjects
TARGET = AmneziaVPN
TEMPLATE = app
@@ -8,42 +8,40 @@ DEFINES += QT_DEPRECATED_WARNINGS
include("3rd/QtSsh/src/ssh/ssh.pri")
include("3rd/QtSsh/src/botan/botan.pri")
include("3rd/SingleApplication/singleapplication.pri")
HEADERS += \
communicator.h \
../ipc/ipc.h \
core/defs.h \
core/errorstrings.h \
core/ipcclient.h \
core/openvpnconfigurator.h \
core/servercontroller.h \
debug.h \
defines.h \
localclient.h \
managementserver.h \
message.h \
protocols/shadowsocksvpnprotocol.h \
runguard.h \
settings.h \
ui/Controls/SlidingStackedWidget.h \
ui/mainwindow.h \
ui/qautostart.h \
utils.h \
vpnconnection.h \
protocols/vpnprotocol.h \
protocols/openvpnprotocol.h \
SOURCES += \
communicator.cpp \
core/ipcclient.cpp \
core/openvpnconfigurator.cpp \
core/servercontroller.cpp \
debug.cpp \
localclient.cpp \
main.cpp \
managementserver.cpp \
message.cpp \
protocols/shadowsocksvpnprotocol.cpp \
runguard.cpp \
settings.cpp \
ui/Controls/SlidingStackedWidget.cpp \
ui/mainwindow.cpp \
ui/qautostart.cpp \
utils.cpp \
vpnconnection.cpp \
protocols/vpnprotocol.cpp \
@@ -100,3 +98,6 @@ macx {
LIBS += -framework Cocoa -framework ApplicationServices -framework CoreServices -framework Foundation -framework AppKit
}
REPC_REPLICA += ../ipc/ipcinterface.rep

View File

@@ -1,79 +0,0 @@
#include "communicator.h"
#include "defines.h"
#include "localclient.h"
#include "utils.h"
Communicator::Communicator(QObject* parent) : QObject(parent),
m_localClient(nullptr)
{
connectToServer();
}
Communicator::~Communicator()
{
}
void Communicator::connectToServer()
{
if (m_localClient) {
delete m_localClient;
}
m_localClient = new LocalClient(this);
connect(m_localClient, &LocalClient::connected, this, &Communicator::onConnected);
connect(m_localClient, &LocalClient::lineAvailable, this, &Communicator::onLineAvailable);
m_localClient->connectToServer(Utils::serverName());
}
void Communicator::onConnected()
{
qDebug().noquote() << QString("Connected to local server '%1'").arg(m_localClient->serverName());
Message message(Message::State::Initialize, QStringList({"Client"}));
sendMessage(message);
}
void Communicator::onLineAvailable(const QString& line)
{
Message message(line);
if (!message.isValid()) {
qDebug() << "Message is not valid";
return;
}
emit messageReceived(message);
}
bool Communicator::isConnected() const
{
if (!m_localClient) {
return false;
}
return m_localClient->connectedState();
}
QString Communicator::readData()
{
return QString();
}
bool Communicator::writeData(const QString& data)
{
return m_localClient->write(data.toUtf8());
}
void Communicator::sendMessage(const Message& message)
{
if (!isConnected()) {
return;
}
const QString data = message.toString();
bool status = writeData(data + "\n");
qDebug().noquote() << QString("Send message '%1',%2 status '%2'").
arg(static_cast<int>(message.state())).
arg(data).
arg(Utils::toString(status));
}

View File

@@ -1,41 +0,0 @@
#ifndef COMMUNICATOR_H
#define COMMUNICATOR_H
#include <QObject>
#include <QStringList>
#include "message.h"
class LocalClient;
class Communicator : public QObject
{
Q_OBJECT
public:
explicit Communicator(QObject* parent = nullptr);
~Communicator();
bool isConnected() const;
void sendMessage(const Message& message);
signals:
void messageReceived(const Message& message);
void comminicatorConnected();
void comminicatorDisconnected();
protected slots:
void onConnected();
void onLineAvailable(const QString& line);
protected:
QString readData();
bool writeData(const QString& data);
void connectToServer();
LocalClient* m_localClient;
};
#endif // COMMUNICATOR_H

View File

@@ -34,8 +34,9 @@ enum ErrorCode
InternalError,
NotImplementedError,
// Server errorz
// Server errors
ServerCheckFailed,
ServerPortAlreadyAllocatedError,
// Ssh connection errors
SshSocketError, SshTimeoutError, SshProtocolError,
@@ -55,6 +56,7 @@ enum ErrorCode
// Distro errors
OpenVpnExecutableMissing,
EasyRsaExecutableMissing,
ShadowSocksExecutableMissing,
AmneziaServiceConnectionFailed,
// VPN errors
@@ -62,6 +64,15 @@ enum ErrorCode
OpenVpnUnknownError
};
namespace config {
// config keys
static QString key_openvpn_config_data() { return "openvpn_config_data"; }
static QString key_openvpn_config_path() { return "openvpn_config_path"; }
static QString key_shadowsocks_config_data() { return "shadowsocks_config_data"; }
}
} // namespace amnezia
#endif // DEFS_H

View File

@@ -11,7 +11,10 @@ static QString errorString(ErrorCode code){
case(NoError): return QObject::tr("No error");
case(UnknownError): return QObject::tr("Unknown Error");
case(NotImplementedError): return QObject::tr("Function not implemented");
// Server errors
case(ServerCheckFailed): return QObject::tr("Server check failed");
case(ServerPortAlreadyAllocatedError): return QObject::tr("Server port already used. Check for another software");
// Ssh connection errors
case(SshSocketError): return QObject::tr("Ssh connection error");

79
client/core/ipcclient.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include "ipcclient.h"
#include <QRemoteObjectNode>
IpcClient &IpcClient::Instance()
{
static IpcClient s;
return s;
}
bool IpcClient::init()
{
Instance().m_localSocket->waitForConnected();
if (!Instance().m_ipcClient) {
qDebug() << "IpcClient::init failed";
return false;
}
return Instance().m_ipcClient->isReplicaValid();
}
QSharedPointer<IpcProcessInterfaceReplica> IpcClient::CreatePrivilegedProcess()
{
if (! Instance().m_ipcClient || ! Instance().m_ipcClient->isReplicaValid()) {
qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid";
return nullptr;
}
QRemoteObjectPendingReply<int> futureResult = Instance().m_ipcClient->createPrivilegedProcess();
futureResult.waitForFinished(1000);
int pid = futureResult.returnValue();
auto pd = QSharedPointer<ProcessDescriptor>(new ProcessDescriptor());
Instance().m_processNodes.insert(pid, pd);
pd->localSocket.reset(new QLocalSocket(pd->replicaNode.data()));
connect(pd->localSocket.data(), &QLocalSocket::connected, pd->replicaNode.data(), [pd]() {
pd->replicaNode->addClientSideConnection(pd->localSocket.data());
pd->ipcProcess.reset(pd->replicaNode->acquire<IpcProcessInterfaceReplica>());
if (!pd->ipcProcess) {
qWarning() << "Acquire IpcProcessInterfaceReplica failed";
}
else {
pd->ipcProcess->waitForSource(1000);
if (!pd->ipcProcess->isReplicaValid()) {
qWarning() << "IpcProcessInterfaceReplica replica is not connected!";
}
connect(pd->ipcProcess.data(), &IpcProcessInterfaceReplica::destroyed, pd->ipcProcess.data(), [pd](){
pd->replicaNode->deleteLater();
});
}
});
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
pd->localSocket->waitForConnected();
auto proccessReplica = QSharedPointer<IpcProcessInterfaceReplica>(pd->ipcProcess);
return proccessReplica;
}
IpcClient::IpcClient(QObject *parent) : QObject(parent)
{
m_localSocket.reset(new QLocalSocket(this));
connect(m_localSocket.data(), &QLocalSocket::connected, &m_ClientNode, [this]() {
m_ClientNode.addClientSideConnection(m_localSocket.data());
m_ipcClient.reset(m_ClientNode.acquire<IpcInterfaceReplica>());
m_ipcClient->waitForSource(1000);
if (!m_ipcClient->isReplicaValid()) {
qWarning() << "IpcClient replica is not connected!";
}
});
m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
}

42
client/core/ipcclient.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef IPCCLIENT_H
#define IPCCLIENT_H
#include <QLocalSocket>
#include <QObject>
#include "ipc.h"
#include "rep_ipcinterface_replica.h"
class IpcClient : public QObject
{
Q_OBJECT
public:
static IpcClient &Instance();
static bool init();
static QSharedPointer<IpcInterfaceReplica> Interface() { return Instance().m_ipcClient; }
static QSharedPointer<IpcProcessInterfaceReplica> CreatePrivilegedProcess();
signals:
private:
explicit IpcClient(QObject *parent = nullptr);
QRemoteObjectNode m_ClientNode;
QSharedPointer<IpcInterfaceReplica> m_ipcClient;
QSharedPointer<QLocalSocket> m_localSocket;
struct ProcessDescriptor {
ProcessDescriptor () {
replicaNode = QSharedPointer<QRemoteObjectNode>(new QRemoteObjectNode());
ipcProcess = QSharedPointer<IpcProcessInterfaceReplica>();
localSocket = QSharedPointer<QLocalSocket>();
}
QSharedPointer<IpcProcessInterfaceReplica> ipcProcess;
QSharedPointer<QRemoteObjectNode> replicaNode;
QSharedPointer<QLocalSocket> localSocket;
};
QMap<int, QSharedPointer<ProcessDescriptor>> m_processNodes;
};
#endif // IPCCLIENT_H

View File

@@ -195,6 +195,12 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
Settings &OpenVpnConfigurator::m_settings()
{
static Settings s;
return s;
}
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials,
Protocol proto, ErrorCode *errorCode)
{
@@ -217,6 +223,13 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
config.replace("$LOCAL_PROXY_PORT", QString::number(ServerController::ssContainerPort()));
}
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
if (m_settings().customRouting()) {
config.replace("redirect-gateway def1 bypass-dhcp", "");
}
config.replace("$REMOTE_HOST", connData.host);
config.replace("$REMOTE_PORT", "1194");
config.replace("$CA_CERT", connData.caCert);
@@ -224,5 +237,9 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
config.replace("$PRIV_KEY", connData.privKey);
config.replace("$TA_KEY", connData.taKey);
#ifdef Q_OS_MAC
config.replace("block-outside-dns", "");
#endif
//qDebug().noquote() << config;
return config;
}

View File

@@ -5,6 +5,7 @@
#include <QProcessEnvironment>
#include "defs.h"
#include "settings.h"
#include "servercontroller.h"
@@ -37,6 +38,8 @@ private:
static ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials,
Protocol proto, ErrorCode *errorCode = nullptr);
static Settings &m_settings();
};
#endif // OPENVPNCONFIGURATOR_H

View File

@@ -7,6 +7,7 @@
#include <QTimer>
#include <QJsonObject>
#include <QJsonDocument>
#include <QApplication>
#include "sshconnectionmanager.h"
@@ -23,7 +24,9 @@ QString ServerController::getContainerName(DockerContainer container)
}
ErrorCode ServerController::runScript(DockerContainer container,
const SshConnectionParameters &sshParams, QString script)
const SshConnectionParameters &sshParams, QString script,
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdOut,
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdErr)
{
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
@@ -66,18 +69,20 @@ ErrorCode ServerController::runScript(DockerContainer container,
wait.quit();
});
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, &wait, [proc, cbReadStdOut](){
QString s = proc->readAllStandardOutput();
if (s != "." && !s.isEmpty()) {
qDebug().noquote() << s;
qDebug().noquote() << "stdout" << s;
}
if (cbReadStdOut) cbReadStdOut(s, proc);
});
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
QString s = proc->readAllStandardError();
if (s != "." && !s.isEmpty()) {
qDebug().noquote() << s;
qDebug().noquote() << "stderr" << s;
}
if (cbReadStdErr) cbReadStdErr(s, proc);
});
proc->start();
@@ -119,9 +124,9 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
QEventLoop wait;
int exitStatus = -1;
// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){
// qDebug() << "Command started";
// });
// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){
// qDebug() << "uploadTextFileToContainer started";
// });
QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){
//qDebug() << "Remote process exited with status" << status;
@@ -138,7 +143,6 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
});
proc->start();
//wait.exec();
if (exitStatus < 0) {
wait.exec();
@@ -272,11 +276,12 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P
QString scriptFileName;
DockerContainer container;
ErrorCode errorCode;
if (proto == Protocol::Any) {
removeServer(credentials, Protocol::OpenVpn);
removeServer(credentials, Protocol::ShadowSocks);
return ErrorCode::NoError;
ErrorCode e = removeServer(credentials, Protocol::OpenVpn);
if (e) {
return e;
}
return removeServer(credentials, Protocol::ShadowSocks);
}
else if (proto == Protocol::OpenVpn) {
scriptFileName = ":/server_scripts/remove_container.sh";
@@ -303,20 +308,21 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P
ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Protocol proto)
{
if (proto == Protocol::OpenVpn) {
return setupOpenVpnServer(credentials);
return ErrorCode::NoError;
//return setupOpenVpnServer(credentials);
}
else if (proto == Protocol::ShadowSocks) {
return setupShadowSocksServer(credentials);
}
else if (proto == Protocol::Any) {
return ErrorCode::NotImplementedError;
//return ErrorCode::NotImplementedError;
// TODO: run concurently
// return setupOpenVpnServer(credentials);
//setupShadowSocksServer(credentials);
//setupOpenVpnServer(credentials);
setupShadowSocksServer(credentials);
}
return ErrorCode::NotImplementedError;
return ErrorCode::NoError;
}
ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credentials)
@@ -329,8 +335,24 @@ ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credenti
scriptData = file.readAll();
if (scriptData.isEmpty()) return ErrorCode::InternalError;
ErrorCode e = runScript(DockerContainer::OpenVpn, sshParams(credentials), scriptData);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
if (data.contains("Automatically restart Docker daemon?")) {
proc->write("yes\n");
}
};
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
};
ErrorCode e = runScript(DockerContainer::OpenVpn, sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr);
if (e) return e;
QApplication::processEvents();
if (stdOut.contains("port is already allocated")) return ErrorCode::ServerPortAlreadyAllocatedError;
if (stdOut.contains("Error response from daemon")) return ErrorCode::ServerCheckFailed;
return checkOpenVpnServer(DockerContainer::OpenVpn, credentials);
}
@@ -346,7 +368,19 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred
scriptData = file.readAll();
if (scriptData.isEmpty()) return ErrorCode::InternalError;
ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
if (data.contains("Automatically restart Docker daemon?")) {
proc->write("yes\n");
}
};
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
};
ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr);
if (e) return e;
// Create ss config
@@ -367,7 +401,7 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred
uploadTextFileToContainer(DockerContainer::ShadowSocks, credentials, configData, sSConfigPath);
// Start ss
QString script = QString("docker exec -i %1 sh -c \"ss-server -c %2 &\"").
QString script = QString("docker exec -d %1 sh -c \"ss-server -c %2\"").
arg(getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath);
e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), script);

View File

@@ -48,7 +48,9 @@ public:
private:
static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams);
static ErrorCode runScript(DockerContainer container,
const QSsh::SshConnectionParameters &sshParams, QString script);
const QSsh::SshConnectionParameters &sshParams, QString script,
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdOut = nullptr,
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr = nullptr);
static ErrorCode setupOpenVpnServer(const ServerCredentials &credentials);
static ErrorCode setupShadowSocksServer(const ServerCredentials &credentials);

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 55 KiB

BIN
client/images/reload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

View File

@@ -1,62 +0,0 @@
#include <QDebug>
#include <QtNetwork>
#include "localclient.h"
LocalClient::LocalClient(QObject *parent) : QObject(parent),
m_socket(new QLocalSocket(this))
{
m_in.setDevice(m_socket);
m_in.setVersion(QDataStream::Qt_5_10);
connect(m_socket, &QLocalSocket::readyRead, this, &LocalClient::onReadyRead);
connect(m_socket, &QLocalSocket::connected, this, &LocalClient::onConnected);
connect(m_socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &LocalClient::displayError);
}
void LocalClient::connectToServer(const QString& name)
{
m_blockSize = 0;
m_socket->abort();
m_socket->connectToServer(name);
}
QString LocalClient::serverName() const
{
return m_socket->serverName();
}
void LocalClient::onConnected()
{
emit connected();
}
bool LocalClient::connectedState() const
{
return (m_socket->state() == QLocalSocket::ConnectedState);
}
quint64 LocalClient::write(const QByteArray& data)
{
return m_socket->write(data);
}
void LocalClient::onReadyRead()
{
if (m_socket->canReadLine()) {
char buf[1024];
qint64 lineLength = m_socket->readLine(buf, sizeof(buf));
if (lineLength != -1) {
QString line = buf;
line = line.simplified();
qDebug().noquote() << QString("Read line: '%1'").arg(line);
emit lineAvailable(line);
}
}
}
void LocalClient::displayError(QLocalSocket::LocalSocketError socketError)
{
Q_UNUSED(socketError)
qDebug().noquote() << QString("The following error occurred: %1.").arg(m_socket->errorString());
}

View File

@@ -1,34 +0,0 @@
#ifndef LOCALCLIENT_H
#define LOCALCLIENT_H
#include <QDataStream>
#include <QLocalSocket>
class LocalClient : public QObject
{
Q_OBJECT
public:
explicit LocalClient(QObject *parent = nullptr);
QString serverName() const;
bool connectedState() const;
quint64 write(const QByteArray& data);
void connectToServer(const QString& name);
signals:
void connected();
void lineAvailable(const QString& line);
private slots:
void displayError(QLocalSocket::LocalSocketError socketError);
void onConnected();
void onReadyRead();
private:
QLocalSocket* m_socket;
QDataStream m_in;
quint32 m_blockSize;
};
#endif // LOCALCLIENT_H

View File

@@ -6,10 +6,14 @@
#include "debug.h"
#include "defines.h"
#include "runguard.h"
#include "singleapplication.h"
#include "ui/mainwindow.h"
#ifdef Q_OS_WIN
#include "Windows.h"
#endif
static void loadTranslator()
{
QTranslator* translator = new QTranslator;
@@ -21,17 +25,19 @@ static void loadTranslator()
int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
RunGuard::instance(APPLICATION_NAME).activate();
QApplication app(argc, argv);
#ifdef Q_OS_WIN
AllowSetForegroundWindow(ASFW_ANY);
#endif
SingleApplication app(argc, argv);
#ifdef Q_OS_WIN
AllowSetForegroundWindow(0);
#endif
loadTranslator();
if (!RunGuard::instance().tryToRun()) {
qDebug() << "Tried to run second instance. Exiting...";
QMessageBox::information(NULL, QObject::tr("Notification"), QObject::tr("AmneziaVPN is already running."));
return 0;
}
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
QFontDatabase::addApplicationFont(":/fonts/Lato-BlackItalic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Lato-Bold.ttf");
@@ -65,5 +71,12 @@ int main(int argc, char *argv[])
MainWindow mainWindow;
mainWindow.show();
if (app.isPrimary()) {
QObject::connect(&app, &SingleApplication::instanceStarted, &mainWindow, [&](){
mainWindow.show();
mainWindow.raise();
});
}
return app.exec();
}

View File

@@ -54,8 +54,7 @@ void ManagementServer::onNewConnection()
void ManagementServer::onSocketError(QAbstractSocket::SocketError socketError)
{
Q_UNUSED(socketError);
Q_UNUSED(socketError)
qDebug().noquote() << QString("Mananement server error: %1").arg(m_socket->errorString());
}

View File

@@ -1,106 +0,0 @@
#include "message.h"
Message::Message(State state, const QStringList& args) :
m_valid(true),
m_state(state),
m_args(args)
{
}
bool Message::isValid() const
{
return m_valid;
}
QString Message::textState() const
{
switch (m_state) {
case State::Unknown: return "Unknown";
case State::Initialize: return "Initialize";
case State::StartRequest: return "StartRequest";
case State::Started: return "Started";
case State::FinishRequest: return "FinishRequest";
case State::Finished: return "Finished";
case State::RoutesAddRequest: return "RoutesAddRequest";
case State::RouteDeleteRequest: return "RouteDeleteRequest";
case State::ClearSavedRoutesRequest: return "ClearSavedRoutesRequest";
case State::FlushDnsRequest: return "FlushDnsRequest";
case State::InstallDriverRequest: return "InstallDriverRequest";
default:
;
}
return QString();
}
QString Message::rawData() const
{
return m_rawData;
}
Message::State Message::state() const
{
return m_state;
}
QString Message::toString() const
{
if (!isValid()) {
return QString();
}
return QString("%1%2%3")
.arg(textState())
.arg(m_dataSeparator)
.arg(argsToString());
}
QString Message::argAtIndex(int index) const
{
if ((index + 1) > args().size()) {
return QString();
}
return args().at(index);
}
QStringList Message::args() const
{
return m_args;
}
QString Message::argsToString() const
{
return m_args.join(m_argSeparator);
}
Message::Message(const QString& data)
{
m_rawData = data;
m_valid = false;
if (data.isEmpty()) {
return;
}
QStringList dataList = data.split(m_dataSeparator);
if ((dataList.size() != 2)) {
return;
}
bool stateFound = false;
for (int i = static_cast<int>(State::Unknown); i <= static_cast<int>(State::InstallDriverRequest); i++ ) {
m_state = static_cast<State>(i);
if (textState() == dataList.at(0)) {
stateFound = true;
break;
}
}
if (!stateFound) {
return;
}
m_args = dataList.at(1).split(m_argSeparator);
m_valid = true;
}

View File

@@ -1,34 +0,0 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QStringList>
class Message {
public:
enum class State {Unknown, Initialize, StartRequest, Started, FinishRequest, Finished,
RoutesAddRequest, RouteDeleteRequest, ClearSavedRoutesRequest, FlushDnsRequest, InstallDriverRequest};
Message(State state, const QStringList& args);
Message(const QString& data);
QString argAtIndex(int index) const;
QString argsToString() const;
QString toString() const;
QStringList args() const;
State state() const;
bool isValid() const;
QString rawData() const;
protected:
QString textState() const;
const QString m_argSeparator = ",";
const QString m_dataSeparator = "|";
bool m_valid;
State m_state;
QStringList m_args;
QString m_rawData;
};
#endif // MESSAGE_H

View File

@@ -4,18 +4,14 @@
#include <QRegularExpression>
#include <QTcpSocket>
#include "communicator.h"
#include "debug.h"
#include "openvpnprotocol.h"
#include "utils.h"
OpenVpnProtocol::OpenVpnProtocol(const QString& args, QObject* parent) :
VpnProtocol(args, parent),
m_requestFromUserToStop(false)
OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject* parent) :
VpnProtocol(configuration, parent)
{
setConfigFile(args);
connect(m_communicator, &Communicator::messageReceived, this, &OpenVpnProtocol::onMessageReceived);
readOpenVpnConfiguration(configuration);
connect(&m_managementServer, &ManagementServer::readyRead, this, &OpenVpnProtocol::onReadyReadDataFromManagementServer);
}
@@ -25,35 +21,14 @@ OpenVpnProtocol::~OpenVpnProtocol()
OpenVpnProtocol::stop();
}
void OpenVpnProtocol::onMessageReceived(const Message& message)
{
if (!message.isValid()) {
qWarning().noquote() << QString("Message received: '%1', but it is not valid").arg(message.toString());
return;
}
switch (message.state()) {
case Message::State::Started:
qDebug() << "OpenVPN process started";
break;
case Message::State::Finished:
qDebug().noquote() << QString("OpenVPN process finished with status %1").arg(message.argAtIndex(1));
onOpenVpnProcessFinished(message.argAtIndex(1).toInt());
break;
default:
qDebug().noquote() << QString("Message received: '%1'").arg(message.toString());
;
}
}
void OpenVpnProtocol::stop()
{
// TODO: need refactoring
// sendTermSignal() will evet return true while server connected
// sendTermSignal() will even return true while server connected ???
if ((m_connectionState == VpnProtocol::ConnectionState::Preparing) ||
(m_connectionState == VpnProtocol::ConnectionState::Connecting) ||
(m_connectionState == VpnProtocol::ConnectionState::Connected) ||
(m_connectionState == VpnProtocol::ConnectionState::TunnelReconnecting)) {
(m_connectionState == VpnProtocol::ConnectionState::Reconnecting)) {
if (!sendTermSignal()) {
killOpenVpnProcess();
}
@@ -61,27 +36,51 @@ void OpenVpnProtocol::stop()
}
}
void OpenVpnProtocol::killOpenVpnProcess()
ErrorCode OpenVpnProtocol::checkAndSetupTapDriver()
{
// send command to kill openvpn process (if any).
if (!IpcClient::Interface()) {
return ErrorCode::AmneziaServiceConnectionFailed;
}
QRemoteObjectPendingReply<QStringList> resultCheck = IpcClient::Interface()->getTapList();
resultCheck.waitForFinished();
if (resultCheck.returnValue().isEmpty()){
QRemoteObjectPendingReply<bool> resultInstall = IpcClient::Interface()->checkAndInstallDriver();
resultInstall.waitForFinished();
if (!resultInstall.returnValue()) return ErrorCode::OpenVpnUnknownError;
}
return ErrorCode::NoError;
}
bool OpenVpnProtocol::setConfigFile(const QString& configFileNamePath)
void OpenVpnProtocol::killOpenVpnProcess()
{
m_configFileName = configFileNamePath;
QFileInfo file(m_configFileName);
if (file.fileName().isEmpty()) {
m_configFileName = Utils::defaultVpnConfigFileName();
if (m_openVpnProcess){
m_openVpnProcess->close();
}
}
if (m_configFileName.isEmpty()) {
return false;
void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration)
{
if (configuration.contains(config::key_openvpn_config_data())) {
m_configFile.open();
m_configFile.write(configuration.value(config::key_openvpn_config_data()).toString().toUtf8());
m_configFile.close();
m_configFileName = m_configFile.fileName();
qDebug().noquote() << QString("Set config data") << m_configFileName;
}
else if (configuration.contains(config::key_openvpn_config_path())) {
m_configFileName = configuration.value(config::key_openvpn_config_path()).toString();
QFileInfo file(m_configFileName);
qDebug().noquote() << QString("Set config file: '%1'").arg(configPath());
if (file.fileName().isEmpty()) {
m_configFileName = Utils::defaultVpnConfigFileName();
}
return false;
qDebug().noquote() << QString("Set config file: '%1'").arg(configPath());
}
}
bool OpenVpnProtocol::openVpnProcessIsRunning() const
@@ -99,17 +98,18 @@ QString OpenVpnProtocol::configPath() const
return m_configFileName;
}
void OpenVpnProtocol::writeCommand(const QString& command)
void OpenVpnProtocol::sendManagementCommand(const QString& command)
{
QIODevice *device = dynamic_cast<QIODevice*>(m_managementServer.socket().data());
if (device) {
QTextStream stream(device);
stream << command << endl;
stream << command << Qt::endl;
}
}
void OpenVpnProtocol::updateRouteGateway(QString line)
{
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1);
if (!line.contains("/")) return;
m_routeGateway = line.split("/", QString::SkipEmptyParts).first();
@@ -128,17 +128,9 @@ QString OpenVpnProtocol::openVpnExecPath() const
ErrorCode OpenVpnProtocol::start()
{
qDebug() << "Start OpenVPN connection";
m_requestFromUserToStop = false;
m_openVpnStateSigTermHandlerTimer.stop();
//qDebug() << "Start OpenVPN connection";
OpenVpnProtocol::stop();
if (communicator() && !communicator()->isConnected()) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return lastError();
}
if (!QFileInfo::exists(openVpnExecPath())) {
setLastError(ErrorCode::OpenVpnExecutableMissing);
return lastError();
@@ -152,39 +144,55 @@ ErrorCode OpenVpnProtocol::start()
QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
Utils::createEmptyFile(vpnLogFileNamePath);
QStringList args({openVpnExecPath(),
"--config" , configPath(),
"--management", m_managementHost, QString::number(m_managementPort),
"--management-client",
"--log-append", vpnLogFileNamePath
});
if (!m_managementServer.start(m_managementHost, m_managementPort)) {
setLastError(ErrorCode::OpenVpnManagementServerError);
return lastError();
}
setConnectionState(ConnectionState::Connecting);
m_communicator->sendMessage(Message(Message::State::StartRequest, args));
startTimeoutTimer();
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
if (!m_openVpnProcess) {
//qWarning() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->waitForSource(1000);
if (!m_openVpnProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->setProgram(openVpnExecPath());
QStringList arguments({"--config" , configPath(),
"--management", m_managementHost, QString::number(m_managementPort),
"--management-client",
"--log-append", vpnLogFileNamePath
});
m_openVpnProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::errorOccurred, [&](QProcess::ProcessError error) {
qDebug() << "IpcProcessInterfaceReplica errorOccurred" << error;
});
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::stateChanged, [&](QProcess::ProcessState newState) {
qDebug() << "IpcProcessInterfaceReplica stateChanged" << newState;
});
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::finished, this, [&]() {
setConnectionState(ConnectionState::Disconnected);
});
m_openVpnProcess->start();
//startTimeoutTimer();
return ErrorCode::NoError;
}
void OpenVpnProtocol::openVpnStateSigTermHandlerTimerEvent()
{
bool processStatus = openVpnProcessIsRunning();
if (processStatus) {
killOpenVpnProcess();
}
onOpenVpnProcessFinished(0);
}
void OpenVpnProtocol::openVpnStateSigTermHandler()
{
m_openVpnStateSigTermHandlerTimer.start(5000);
}
bool OpenVpnProtocol::sendTermSignal()
{
return m_managementServer.writeCommand("signal SIGTERM");
@@ -220,14 +228,14 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
if (line.contains("CONNECTED,SUCCESS")) {
sendByteCount();
stopTimeoutTimer();
updateVpnGateway();
setConnectionState(VpnProtocol::ConnectionState::Connected);
continue;
} else if (line.contains("EXITING,SIGTER")) {
openVpnStateSigTermHandler();
//openVpnStateSigTermHandler();
setConnectionState(VpnProtocol::ConnectionState::Disconnecting);
continue;
} else if (line.contains("RECONNECTING")) {
setConnectionState(VpnProtocol::ConnectionState::TunnelReconnecting);
setConnectionState(VpnProtocol::ConnectionState::Reconnecting);
continue;
}
}
@@ -236,6 +244,10 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
updateRouteGateway(line);
}
if (line.contains("PUSH: Received control message")) {
updateVpnGateway(line);
}
if (line.contains("FATAL")) {
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
@@ -262,59 +274,61 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
}
}
void OpenVpnProtocol::onOpenVpnProcessFinished(int exitCode)
void OpenVpnProtocol::updateVpnGateway(const QString &line)
{
m_openVpnStateSigTermHandlerTimer.stop();
if (m_connectionState == VpnProtocol::ConnectionState::Disconnected) {
qDebug() << "Already in disconnected state";
return;
}
// line looks like
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
qDebug().noquote() << QString("Process finished with code: %1").arg(exitCode);
QStringList params = line.split(",");
for (const QString &l : params) {
if (l.contains("ifconfig")) {
if (l.split(" ").size() == 3) {
m_vpnAddress = l.split(" ").at(1);
m_vpnGateway = l.split(" ").at(2);
setConnectionState(VpnProtocol::ConnectionState::Disconnected);
}
void OpenVpnProtocol::updateVpnGateway()
{
QProcess ipconfig;
ipconfig.start("ipconfig", QStringList() << "/all");
ipconfig.waitForStarted();
ipconfig.waitForFinished();
QString d = ipconfig.readAll();
d.replace("\r", "");
//qDebug().noquote() << d;
QStringList adapters = d.split(":\n");
bool isTapV9Present = false;
QString tapV9;
for (int i = 0; i < adapters.size(); ++i) {
if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
isTapV9Present = true;
tapV9 = adapters.at(i);
break;
qDebug() << QString("Set vpn address %1, gw %2").arg(m_vpnAddress).arg(vpnGateway());
}
}
}
if (!isTapV9Present) {
m_vpnGateway = "";
}
QStringList lines = tapV9.split("\n");
for (int i = 0; i < lines.size(); ++i) {
if (!lines.at(i).contains("DHCP")) continue;
// QProcess ipconfig;
// ipconfig.start("ipconfig", QStringList() << "/all");
// ipconfig.waitForStarted();
// ipconfig.waitForFinished();
QRegularExpression re("(: )([\\d\\.]+)($)");
QRegularExpressionMatch match = re.match(lines.at(i));
// QString d = ipconfig.readAll();
// d.replace("\r", "");
// //qDebug().noquote() << d;
if (match.hasMatch()) {
qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
m_vpnGateway = match.captured(2);
return;
}
else continue;
}
// QStringList adapters = d.split(":\n");
m_vpnGateway = "";
// bool isTapV9Present = false;
// QString tapV9;
// for (int i = 0; i < adapters.size(); ++i) {
// if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
// isTapV9Present = true;
// tapV9 = adapters.at(i);
// break;
// }
// }
// if (!isTapV9Present) {
// m_vpnGateway = "";
// }
// QStringList lines = tapV9.split("\n");
// for (int i = 0; i < lines.size(); ++i) {
// if (!lines.at(i).contains("DHCP")) continue;
// QRegularExpression re("(: )([\\d\\.]+)($)");
// QRegularExpressionMatch match = re.match(lines.at(i));
// if (match.hasMatch()) {
// qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
// m_vpnGateway = match.captured(2);
// return;
// }
// else continue;
// }
// m_vpnGateway = "";
}

View File

@@ -6,51 +6,50 @@
#include <QTimer>
#include "managementserver.h"
#include "message.h"
#include "vpnprotocol.h"
#include "core/ipcclient.h"
class OpenVpnProtocol : public VpnProtocol
{
Q_OBJECT
public:
explicit OpenVpnProtocol(const QString& args = QString(), QObject* parent = nullptr);
explicit OpenVpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~OpenVpnProtocol() override;
ErrorCode start() override;
void stop() override;
ErrorCode checkAndSetupTapDriver();
protected slots:
void onMessageReceived(const Message& message);
void onOpenVpnProcessFinished(int exitCode);
void onReadyReadDataFromManagementServer();
protected:
private:
QString configPath() const;
QString openVpnExecPath() const;
bool openVpnProcessIsRunning() const;
bool sendTermSignal();
bool setConfigFile(const QString& configFileNamePath);
void readOpenVpnConfiguration(const QJsonObject &configuration);
void disconnectFromManagementServer();
void killOpenVpnProcess();
void openVpnStateSigTermHandler();
void openVpnStateSigTermHandlerTimerEvent();
void sendByteCount();
void sendInitialData();
void writeCommand(const QString& command);
void sendManagementCommand(const QString& command);
const QString m_managementHost = "127.0.0.1";
const unsigned int m_managementPort = 57775;
ManagementServer m_managementServer;
QString m_configFileName;
QTimer m_openVpnStateSigTermHandlerTimer;
bool m_requestFromUserToStop;
QTemporaryFile m_configFile;
private:
void updateRouteGateway(QString line);
void updateVpnGateway();
void updateVpnGateway(const QString &line);
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
};
#endif // OPENVPNPROTOCOL_H

View File

@@ -1,49 +1,59 @@
#include "shadowsocksvpnprotocol.h"
#include "core/servercontroller.h"
#include "communicator.h"
#include "debug.h"
#include "utils.h"
#include <QJsonDocument>
#include <QJsonObject>
ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QString &args, QObject *parent):
OpenVpnProtocol(args, parent)
ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration, QObject *parent):
OpenVpnProtocol(configuration, parent)
{
m_shadowSocksConfig = args;
readShadowSocksConfiguration(configuration);
}
ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol()
{
qDebug() << "ShadowSocksVpnProtocol::stop()";
ShadowSocksVpnProtocol::stop();
}
ErrorCode ShadowSocksVpnProtocol::start()
{
qDebug() << "ShadowSocksVpnProtocol::start()";
QJsonObject config = QJsonDocument::fromJson(m_shadowSocksConfig.toUtf8()).object();
ssProcess.setProcessChannelMode(QProcess::MergedChannels);
m_ssProcess.setProcessChannelMode(QProcess::MergedChannels);
ssProcess.setProgram(shadowSocksExecPath());
ssProcess.setArguments(QStringList() << "-s" << config.value("server").toString()
<< "-p" << QString::number(config.value("server_port").toInt())
<< "-l" << QString::number(config.value("local_port").toInt())
<< "-m" << config.value("method").toString()
<< "-k" << config.value("password").toString()
m_ssProcess.setProgram(shadowSocksExecPath());
m_ssProcess.setArguments(QStringList() << "-s" << m_shadowSocksConfig.value("server").toString()
<< "-p" << QString::number(m_shadowSocksConfig.value("server_port").toInt())
<< "-l" << QString::number(m_shadowSocksConfig.value("local_port").toInt())
<< "-m" << m_shadowSocksConfig.value("method").toString()
<< "-k" << m_shadowSocksConfig.value("password").toString()
);
ssProcess.start();
ssProcess.waitForStarted();
connect(&m_ssProcess, &QProcess::readyRead, this, [this](){
qDebug().noquote() << m_ssProcess.readAll();
});
if (ssProcess.state() == QProcess::ProcessState::Running) {
m_ssProcess.start();
m_ssProcess.waitForStarted();
if (m_ssProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(ConnectionState::Connecting);
return OpenVpnProtocol::start();
}
else return ErrorCode::FailedToStartRemoteProcessError;
else return ErrorCode::ShadowSocksExecutableMissing;
}
void ShadowSocksVpnProtocol::stop()
{
OpenVpnProtocol::stop();
qDebug() << "ShadowSocksVpnProtocol::stop()";
ssProcess.kill();
m_ssProcess.close();
}
QString ShadowSocksVpnProtocol::shadowSocksExecPath() const
@@ -55,7 +65,7 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath() const
#endif
}
QString ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto)
QJsonObject ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto)
{
QJsonObject ssConfig;
ssConfig.insert("server", credentials.hostName);
@@ -64,5 +74,10 @@ QString ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials &cr
ssConfig.insert("password", credentials.password);
ssConfig.insert("timeout", 60);
ssConfig.insert("method", ServerController::ssEncryption());
return QJsonDocument(ssConfig).toJson();
return ssConfig;
}
void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration)
{
m_shadowSocksConfig = configuration.value(config::key_shadowsocks_config_data()).toObject();
}

View File

@@ -7,21 +7,23 @@
class ShadowSocksVpnProtocol : public OpenVpnProtocol
{
public:
ShadowSocksVpnProtocol(const QString& args = QString(), QObject* parent = nullptr);
ShadowSocksVpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~ShadowSocksVpnProtocol() override;
ErrorCode start() override;
void stop() override;
static QString genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto = Protocol::ShadowSocks);
static QJsonObject genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto = Protocol::ShadowSocks);
protected:
void readShadowSocksConfiguration(const QJsonObject &configuration);
QString shadowSocksExecPath() const;
protected:
QString m_shadowSocksConfig;
QJsonObject m_shadowSocksConfig;
private:
QProcess ssProcess;
QProcess m_ssProcess;
};
#endif // SHADOWSOCKSVPNPROTOCOL_H

View File

@@ -1,42 +1,26 @@
#include <QDebug>
#include <QTimer>
#include "communicator.h"
#include "vpnprotocol.h"
#include "core/errorstrings.h"
Communicator* VpnProtocol::m_communicator = nullptr;
VpnProtocol::VpnProtocol(const QString& args, QObject* parent)
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject* parent)
: QObject(parent),
m_connectionState(ConnectionState::Unknown),
m_rawConfig(configuration),
m_timeoutTimer(new QTimer(this)),
m_receivedBytes(0),
m_sentBytes(0)
{
m_timeoutTimer->setSingleShot(true);
connect(m_timeoutTimer, &QTimer::timeout, this, &VpnProtocol::onTimeout);
Q_UNUSED(args)
}
void VpnProtocol::initializeCommunicator(QObject* parent)
{
if (!m_communicator) {
m_communicator = new Communicator(parent);
}
}
Communicator* VpnProtocol::communicator()
{
return m_communicator;
}
void VpnProtocol::setLastError(ErrorCode lastError)
{
m_lastError = lastError;
if (lastError){
setConnectionState(ConnectionState::Disconnected);
setConnectionState(ConnectionState::Error);
}
qCritical().noquote() << "VpnProtocol error, code" << m_lastError << errorString(m_lastError);
}
@@ -79,9 +63,14 @@ void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes)
void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state)
{
qDebug() << "VpnProtocol::setConnectionState" << textConnectionState(state);
if (m_connectionState == state) {
return;
}
if (m_connectionState == ConnectionState::Disconnected && state == ConnectionState::Disconnecting) {
return;
}
m_connectionState = state;
if (m_connectionState == ConnectionState::Disconnected) {
@@ -113,7 +102,7 @@ QString VpnProtocol::textConnectionState(ConnectionState connectionState)
case ConnectionState::Connecting: return tr("Connecting...");
case ConnectionState::Connected: return tr("Connected");
case ConnectionState::Disconnecting: return tr("Disconnecting...");
case ConnectionState::TunnelReconnecting: return tr("Reconnecting...");
case ConnectionState::Reconnecting: return tr("Reconnecting...");
case ConnectionState::Error: return tr("Error");
default:
;
@@ -127,12 +116,12 @@ QString VpnProtocol::textConnectionState() const
return textConnectionState(m_connectionState);
}
bool VpnProtocol::onConnected() const
bool VpnProtocol::isConnected() const
{
return m_connectionState == ConnectionState::Connected;
}
bool VpnProtocol::onDisconnected() const
bool VpnProtocol::isDisconnected() const
{
return m_connectionState == ConnectionState::Disconnected;
}

View File

@@ -3,30 +3,29 @@
#include <QObject>
#include <QString>
#include <QJsonObject>
#include "core/defs.h"
using namespace amnezia;
class QTimer;
class Communicator;
class VpnProtocol : public QObject
{
Q_OBJECT
public:
explicit VpnProtocol(const QString& args = QString(), QObject* parent = nullptr);
explicit VpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~VpnProtocol() override = default;
enum class ConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, TunnelReconnecting, Error};
enum ConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, Reconnecting, Error};
Q_ENUM(ConnectionState)
static Communicator* communicator();
static QString textConnectionState(ConnectionState connectionState);
static void initializeCommunicator(QObject* parent = nullptr);
virtual bool onConnected() const;
virtual bool onDisconnected() const;
virtual bool isConnected() const;
virtual bool isDisconnected() const;
virtual ErrorCode start() = 0;
virtual void stop() = 0;
@@ -54,18 +53,18 @@ protected:
virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes);
virtual void setConnectionState(VpnProtocol::ConnectionState state);
static Communicator* m_communicator;
ConnectionState m_connectionState;
QString m_routeGateway;
QString m_vpnAddress;
QString m_vpnGateway;
QJsonObject m_rawConfig;
private:
QTimer* m_timeoutTimer;
ErrorCode m_lastError;
quint64 m_receivedBytes;
quint64 m_sentBytes;
};
#endif // VPNPROTOCOL_H

View File

@@ -40,5 +40,6 @@
<file>server_scripts/setup_shadowsocks_server.sh</file>
<file>server_scripts/template_shadowsocks.ovpn</file>
<file>server_scripts/setup_firewall.sh</file>
<file>images/reload.png</file>
</qresource>
</RCC>

View File

@@ -1,88 +0,0 @@
#include "runguard.h"
#include <QCryptographicHash>
namespace
{
QString generateKeyHash( const QString& key, const QString& salt )
{
QByteArray data;
data.append( key.toUtf8() );
data.append( salt.toUtf8() );
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
return data;
}
}
RunGuard::RunGuard(const QString& key)
: key( key )
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
, sharedMem( sharedmemKey )
, memLock( memLockKey, 1 )
{
qDebug() << "RunGuard::RunGuard key" << key;
}
RunGuard &RunGuard::instance(const QString& key)
{
static RunGuard s(key);
return s;
}
void RunGuard::activate()
{
memLock.acquire();
{
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
memLock.release();
}
RunGuard::~RunGuard()
{
release();
}
bool RunGuard::isAnotherRunning() const
{
if ( sharedMem.isAttached() )
return false;
memLock.acquire();
const bool isRunning = sharedMem.attach();
if ( isRunning )
sharedMem.detach();
memLock.release();
return isRunning;
}
bool RunGuard::tryToRun()
{
if ( isAnotherRunning() ) // Extra check
return false;
memLock.acquire();
const bool result = sharedMem.create( sizeof( quint64 ) );
memLock.release();
if ( !result )
{
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if ( sharedMem.isAttached() )
sharedMem.detach();
memLock.release();
}

View File

@@ -1,37 +0,0 @@
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
#include <QDebug>
/**
* @brief The RunGuard class - The application single instance (via shared memory)
*/
class RunGuard
{
public:
static RunGuard &instance(const QString& key = QString());
~RunGuard();
void activate();
bool isAnotherRunning() const;
bool tryToRun();
void release();
private:
RunGuard(const QString& key);
Q_DISABLE_COPY( RunGuard )
const QString key;
const QString memLockKey;
const QString sharedmemKey;
mutable QSharedMemory sharedMem;
mutable QSystemSemaphore memLock;
};
#endif // RUNGUARD_H

View File

@@ -1,4 +1,5 @@
#CONTAINER_NAME=... this var will be set in ServerController
# CONTAINER_NAME=... this var will be set in ServerController
# Don't run commands in background like sh -c "openvpn &"
apt-get update

View File

@@ -1,4 +1,5 @@
#CONTAINER_NAME=... this var will be set in ServerController
# CONTAINER_NAME=... this var will be set in ServerController
# Don't run commands in background like sh -c "openvpn &"
apt-get update

View File

@@ -12,6 +12,11 @@ tls-client
tls-version-min 1.2
key-direction 1
remote-cert-tls server
redirect-gateway def1 bypass-dhcp
dhcp-option DNS $PRIMARY_DNS
dhcp-option DNS $SECONDARY_DNS
block-outside-dns
remote $REMOTE_HOST $REMOTE_PORT

View File

@@ -12,6 +12,11 @@ tls-client
tls-version-min 1.2
key-direction 1
remote-cert-tls server
redirect-gateway def1 bypass-dhcp
dhcp-option DNS $PRIMARY_DNS
dhcp-option DNS $SECONDARY_DNS
block-outside-dns
socks-proxy 127.0.0.1 $LOCAL_PROXY_PORT
route $REMOTE_HOST 255.255.255.255 net_gateway

View File

@@ -6,27 +6,6 @@ Settings::Settings(QObject* parent) :
QObject(parent),
m_settings (ORGANIZATION_NAME, APPLICATION_NAME, this)
{
read();
}
void Settings::read()
{
m_settings.beginGroup("Server");
m_userName = m_settings.value("userName", QString()).toString();
m_password = m_settings.value("password", QString()).toString();
m_serverName = m_settings.value("serverName", QString()).toString();
m_serverPort = m_settings.value("serverPort", 22).toInt();
m_settings.endGroup();
}
void Settings::save()
{
m_settings.beginGroup("Server");
m_settings.setValue("userName", m_userName);
m_settings.setValue("password", m_password);
m_settings.setValue("serverName", m_serverName);
m_settings.setValue("serverPort", m_serverPort);
m_settings.endGroup();
}
bool Settings::haveAuthData() const
@@ -34,26 +13,6 @@ bool Settings::haveAuthData() const
return (!serverName().isEmpty() && !userName().isEmpty() && !password().isEmpty());
}
void Settings::setUserName(const QString& login)
{
m_userName = login;
}
void Settings::setPassword(const QString& password)
{
m_password = password;
}
void Settings::setServerName(const QString& serverName)
{
m_serverName = serverName;
}
void Settings::setServerPort(int serverPort)
{
m_serverPort = serverPort;
}
void Settings::setServerCredentials(const ServerCredentials &credentials)
{
setServerName(credentials.hostName);

View File

@@ -18,40 +18,53 @@ class Settings : public QObject
public:
explicit Settings(QObject* parent = nullptr);
void read();
void save();
QString userName() const { return m_settings.value("Server/userName", QString()).toString(); }
void setUserName(const QString& login) { m_settings.setValue("Server/userName", login); }
void setUserName(const QString& login);
void setPassword(const QString& password);
void setServerName(const QString& serverName);
void setServerPort(int serverPort = 22);
void setServerCredentials(const ServerCredentials &credentials);
QString password() const { return m_settings.value("Server/password", QString()).toString(); }
void setPassword(const QString& password) { m_settings.setValue("Server/password", password); }
QString serverName() const { return m_settings.value("Server/serverName", QString()).toString(); }
void setServerName(const QString& serverName) { m_settings.setValue("Server/serverName", serverName); }
int serverPort() const { return m_settings.value("Server/serverPort", 22).toInt(); }
void setServerPort(int serverPort = 22) { m_settings.setValue("Server/serverPort", serverPort); }
QString userName() const { return m_userName; }
QString password() const { return m_password; }
QString serverName() const { return m_serverName; }
int serverPort() const { return m_serverPort; }
ServerCredentials serverCredentials();
void setServerCredentials(const ServerCredentials &credentials);
bool haveAuthData() const;
bool isAutoConnect() const { return m_settings.value("Conf/autoConnect", QString()).toBool(); }
void setAutoConnect(bool enabled) { m_settings.setValue("Conf/autoConnect", enabled); }
bool customRouting() const { return m_settings.value("Conf/customRouting", false).toBool(); }
void setCustomRouting(bool customRouting) { m_settings.setValue("Conf/customRouting", customRouting); }
// list of sites to pass blocking added by user
QStringList customSites() { return m_settings.value("customSites").toStringList(); }
void setCustomSites(const QStringList &customSites) { m_settings.setValue("customSites", customSites); }
QStringList customSites() { return m_settings.value("Conf/customSites").toStringList(); }
void setCustomSites(const QStringList &customSites) { m_settings.setValue("Conf/customSites", customSites); }
// list of ips to pass blocking generated from customSites
QStringList customIps() { return m_settings.value("customIps").toStringList(); }
void setCustomIps(const QStringList &customIps) { m_settings.setValue("customIps", customIps); }
QStringList customIps() { return m_settings.value("Conf/customIps").toStringList(); }
void setCustomIps(const QStringList &customIps) { m_settings.setValue("Conf/customIps", customIps); }
QString primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1()).toString(); }
QString secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2()).toString(); }
protected:
//QString primaryDns() const { return m_primaryDns; }
void setPrimaryDns(const QString &primaryDns) { m_settings.setValue("Conf/primaryDns", primaryDns); }
//QString secondaryDns() const { return m_secondaryDns; }
void setSecondaryDns(const QString &secondaryDns) { m_settings.setValue("Conf/secondaryDns", secondaryDns); }
QString cloudFlareNs1() const { return "1.1.1.1"; }
QString cloudFlareNs2() const { return "1.0.0.1"; }
QString openNicNs5() const { return "94.103.153.176"; }
QString openNicNs13() const { return "144.76.103.143"; }
private:
QSettings m_settings;
QString m_userName;
QString m_password;
QString m_serverName;
int m_serverPort;
};
#endif // SETTINGS_H

View File

@@ -1,6 +1,7 @@
#include <QApplication>
#include <QClipboard>
#include <QDesktopServices>
#include <QHBoxLayout>
#include <QJsonDocument>
#include <QJsonObject>
#include <QKeyEvent>
@@ -11,11 +12,10 @@
#include <QThread>
#include <QTimer>
#include "communicator.h"
#include "core/errorstrings.h"
#include "core/openvpnconfigurator.h"
#include "core/servercontroller.h"
#include "ui/qautostart.h"
#include "debug.h"
#include "defines.h"
@@ -46,6 +46,7 @@ MainWindow::MainWindow(QWidget *parent) :
#ifdef Q_OS_MAC
ui->widget_tittlebar->hide();
resize(width(), height() - ui->stackedWidget_main->y());
ui->stackedWidget_main->move(0,0);
fixWidget(this);
#endif
@@ -61,20 +62,13 @@ MainWindow::MainWindow(QWidget *parent) :
setupTray();
setupUiConnections();
customSitesModel = new QStringListModel();
ui->listView_sites_custom->setModel(customSitesModel);
connect(ui->listView_sites_custom, &QListView::doubleClicked, [&](const QModelIndex &index){
QDesktopServices::openUrl("https://" + index.data().toString());
});
connect(ui->lineEdit_sites_add_custom, &QLineEdit::returnPressed, [&](){
ui->pushButton_sites_add_custom->click();
});
initCustomSites();
updateSettings();
ui->pushButton_general_settings_exit->hide();
//ui->pushButton_share_connection->hide();
//ui->pushButton_general_settings_exit->hide();
setFixedSize(width(),height());
@@ -90,7 +84,20 @@ MainWindow::MainWindow(QWidget *parent) :
onConnectionStateChanged(VpnProtocol::ConnectionState::Disconnected);
if (m_settings.isAutoConnect() && m_settings.haveAuthData()) {
QTimer::singleShot(1000, this, [this](){
ui->pushButton_connect->setEnabled(false);
onConnect();
});
}
qDebug().noquote() << QString("Default config: %1").arg(Utils::defaultVpnConfigFileName());
m_ipAddressValidator.setRegExp(Utils::ipAddressRegExp());
ui->lineEdit_new_server_ip->setValidator(&m_ipAddressValidator);
ui->lineEdit_network_settings_dns1->setValidator(&m_ipAddressValidator);
ui->lineEdit_network_settings_dns2->setValidator(&m_ipAddressValidator);
}
MainWindow::~MainWindow()
@@ -101,7 +108,7 @@ MainWindow::~MainWindow()
for (int i = 0; i < 50; i++) {
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
QThread::msleep(100);
if (m_vpnConnection->onDisconnected()) {
if (m_vpnConnection->isDisconnected()) {
break;
}
}
@@ -146,6 +153,8 @@ QWidget *MainWindow::getPageWidget(MainWindow::Page page)
case(Page::NewServer): return ui->page_new_server;
case(Page::Vpn): return ui->page_vpn;
case(Page::GeneralSettings): return ui->page_general_settings;
case(Page::AppSettings): return ui->page_app_settings;
case(Page::NetworkSettings): return ui->page_network_settings;
case(Page::ServerSettings): return ui->page_server_settings;
case(Page::ShareConnection): return ui->page_share_connection;
case(Page::Sites): return ui->page_sites;
@@ -249,7 +258,6 @@ void MainWindow::onPushButtonNewServerConnectWithNewData(bool)
if (ok) {
m_settings.setServerCredentials(serverCredentials);
m_settings.save();
goToPage(Page::Vpn);
qApp->processEvents();
@@ -264,7 +272,6 @@ void MainWindow::onPushButtonNewServerConnectWithExistingCode(bool)
s.replace("vpn://", "");
QJsonObject o = QJsonDocument::fromJson(QByteArray::fromBase64(s.toUtf8())).object();
qDebug().noquote() << QByteArray::fromBase64(s.toUtf8());
ServerCredentials credentials;
credentials.hostName = o.value("h").toString();
credentials.port = o.value("p").toInt();
@@ -272,7 +279,6 @@ void MainWindow::onPushButtonNewServerConnectWithExistingCode(bool)
credentials.password = o.value("w").toString();
m_settings.setServerCredentials(credentials);
m_settings.save();
goToPage(Page::Vpn);
qDebug() << QString("Added server %3@%1:%2").
@@ -299,7 +305,8 @@ bool MainWindow::installServer(ServerCredentials credentials,
timer.start(1000);
ErrorCode e = ServerController::setupServer(credentials, Protocol::OpenVpn);
ErrorCode e = ServerController::setupServer(credentials, Protocol::Any);
qDebug() << "Setup server finished with code" << e;
if (e) {
page->setEnabled(true);
button->setVisible(true);
@@ -377,14 +384,11 @@ void MainWindow::onPushButtonForgetServer(bool)
m_settings.setServerName("");
m_settings.setServerPort();
m_settings.save();
goToPage(Page::Start);
}
void MainWindow::onBytesChanged(quint64 receivedData, quint64 sentData)
{
qDebug() << "MainWindow::onBytesChanged" << receivedData << sentData;
ui->label_speed_received->setText(VpnConnection::bytesPerSecToText(receivedData));
ui->label_speed_sent->setText(VpnConnection::bytesPerSecToText(sentData));
}
@@ -394,6 +398,7 @@ void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state)
qDebug() << "MainWindow::onConnectionStateChanged" << VpnProtocol::textConnectionState(state);
bool pushButtonConnectEnabled = false;
bool radioButtonsModeEnabled = false;
ui->label_state->setText(VpnProtocol::textConnectionState(state));
setTrayState(state);
@@ -403,32 +408,41 @@ void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state)
onBytesChanged(0,0);
ui->pushButton_connect->setChecked(false);
pushButtonConnectEnabled = true;
radioButtonsModeEnabled = true;
break;
case VpnProtocol::ConnectionState::Preparing:
pushButtonConnectEnabled = false;
radioButtonsModeEnabled = false;
break;
case VpnProtocol::ConnectionState::Connecting:
pushButtonConnectEnabled = false;
radioButtonsModeEnabled = false;
break;
case VpnProtocol::ConnectionState::Connected:
pushButtonConnectEnabled = true;
radioButtonsModeEnabled = false;
break;
case VpnProtocol::ConnectionState::Disconnecting:
pushButtonConnectEnabled = false;
radioButtonsModeEnabled = false;
break;
case VpnProtocol::ConnectionState::TunnelReconnecting:
case VpnProtocol::ConnectionState::Reconnecting:
pushButtonConnectEnabled = true;
radioButtonsModeEnabled = false;
break;
case VpnProtocol::ConnectionState::Error:
ui->pushButton_connect->setChecked(false);
pushButtonConnectEnabled = true;
radioButtonsModeEnabled = true;
break;
case VpnProtocol::ConnectionState::Unknown:
default:
pushButtonConnectEnabled = true;
;
radioButtonsModeEnabled = true;
}
ui->pushButton_connect->setEnabled(pushButtonConnectEnabled);
ui->radioButton_mode_all_sites->setEnabled(radioButtonsModeEnabled);
ui->radioButton_mode_selected_sites->setEnabled(radioButtonsModeEnabled);
}
void MainWindow::onVpnProtocolError(ErrorCode errorCode)
@@ -448,9 +462,11 @@ void MainWindow::onPushButtonConnectClicked(bool checked)
void MainWindow::setupTray()
{
m_menu = new QMenu();
//m_menu->setStyleSheet(styleSheet());
m_menu->addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, SLOT(show()));
m_menu->addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, [this](){
show();
raise();
});
m_menu->addSeparator();
m_trayActionConnect = m_menu->addAction(tr("Connect"), this, SLOT(onConnect()));
m_trayActionDisconnect = m_menu->addAction(tr("Disconnect"), this, SLOT(onDisconnect()));
@@ -519,8 +535,10 @@ void MainWindow::setupUiConnections()
connect(ui->pushButton_server_settings_clear, SIGNAL(clicked(bool)), this, SLOT(onPushButtonClearServer(bool)));
connect(ui->pushButton_server_settings_forget, SIGNAL(clicked(bool)), this, SLOT(onPushButtonForgetServer(bool)));
connect(ui->pushButton_blocked_list, &QPushButton::clicked, this, [this](){ goToPage(Page::Sites); });
connect(ui->pushButton_vpn_add_site, &QPushButton::clicked, this, [this](){ goToPage(Page::Sites); });
connect(ui->pushButton_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
connect(ui->pushButton_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::AppSettings); });
connect(ui->pushButton_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::NetworkSettings); });
connect(ui->pushButton_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); });
connect(ui->pushButton_share_connection, &QPushButton::clicked, this, [this](){
goToPage(Page::ShareConnection);
@@ -531,7 +549,7 @@ void MainWindow::setupUiConnections()
QGuiApplication::clipboard()->setText(ui->textEdit_sharing_code->toPlainText());
ui->pushButton_copy_sharing_code->setText(tr("Copied"));
QTimer::singleShot(3000, [this]() {
QTimer::singleShot(3000, this, [this]() {
ui->pushButton_copy_sharing_code->setText(tr("Copy"));
});
});
@@ -540,11 +558,50 @@ void MainWindow::setupUiConnections()
connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); });
connect(ui->pushButton_back_from_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); });
connect(ui->pushButton_back_from_new_server, &QPushButton::clicked, this, [this](){ goToPage(Page::Start); });
connect(ui->pushButton_back_from_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
connect(ui->pushButton_back_from_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
connect(ui->pushButton_back_from_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
connect(ui->pushButton_back_from_share, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
connect(ui->pushButton_sites_add_custom, &QPushButton::clicked, this, [this](){ onPushButtonAddCustomSitesClicked(); });
connect(ui->pushButton_sites_delete_custom, &QPushButton::clicked, this, [this](){ onPushButtonDeleteCustomSiteClicked(); });
connect(ui->radioButton_mode_selected_sites, &QRadioButton::toggled, ui->pushButton_vpn_add_site, &QPushButton::setEnabled);
connect(ui->radioButton_mode_selected_sites, &QRadioButton::toggled, this, [this](bool toggled) {
m_settings.setCustomRouting(toggled);
});
connect(ui->checkBox_autostart, &QCheckBox::stateChanged, this, [this](int state){
if (state == Qt::Unchecked) {
ui->checkBox_autoconnect->setChecked(false);
}
Autostart::setAutostart(state == Qt::Checked);
});
connect(ui->checkBox_autoconnect, &QCheckBox::stateChanged, this, [this](int state){
m_settings.setAutoConnect(state == Qt::Checked);
});
connect(ui->pushButton_network_settings_resetdns1, &QPushButton::clicked, this, [this](){
m_settings.setPrimaryDns(m_settings.cloudFlareNs1());
updateSettings();
});
connect(ui->pushButton_network_settings_resetdns2, &QPushButton::clicked, this, [this](){
m_settings.setPrimaryDns(m_settings.cloudFlareNs2());
updateSettings();
});
connect(ui->lineEdit_network_settings_dns1, &QLineEdit::textEdited, this, [this](const QString &newText){
if (m_ipAddressValidator.regExp().exactMatch(newText)) {
m_settings.setPrimaryDns(newText);
}
});
connect(ui->lineEdit_network_settings_dns2, &QLineEdit::textEdited, this, [this](const QString &newText){
if (m_ipAddressValidator.regExp().exactMatch(newText)) {
m_settings.setSecondaryDns(newText);
}
});
}
void MainWindow::setTrayState(VpnProtocol::ConnectionState state)
@@ -570,7 +627,7 @@ void MainWindow::setTrayState(VpnProtocol::ConnectionState state)
case VpnProtocol::ConnectionState::Disconnecting:
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
break;
case VpnProtocol::ConnectionState::TunnelReconnecting:
case VpnProtocol::ConnectionState::Reconnecting:
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
break;
case VpnProtocol::ConnectionState::Error:
@@ -663,13 +720,13 @@ void MainWindow::onPushButtonAddCustomSitesClicked()
customIps.append(newIp);
m_settings.setCustomIps(customIps);
// add to routes immediatelly
// if (vpnStatus() == VPNStatusConnected) {
// //Router::Instance().routeAdd(newIp, vpnGate());
// }
if (m_vpnConnection->connectionState() == VpnProtocol::ConnectionState::Connected) {
IpcClient::Interface()->routeAddList(m_vpnConnection->vpnProtocol()->vpnGateway(),
QStringList() << newIp);
}
}
initCustomSites();
updateSettings();
ui->lineEdit_sites_add_custom->clear();
}
@@ -678,11 +735,8 @@ void MainWindow::onPushButtonAddCustomSitesClicked()
}
}
void MainWindow::onPushButtonDeleteCustomSiteClicked()
void MainWindow::onPushButtonDeleteCustomSiteClicked(const QString &siteToDelete)
{
QModelIndex index = ui->listView_sites_custom->currentIndex();
QString siteToDelete = index.data(Qt::DisplayRole).toString();
if (siteToDelete.isEmpty()) {
return;
}
@@ -699,16 +753,30 @@ void MainWindow::onPushButtonDeleteCustomSiteClicked()
qDebug() << "Deleted custom ip:" << ipToDelete;
m_settings.setCustomIps(customIps);
updateSettings();
initCustomSites();
//Router::Instance().routeDelete(Utils::getIPAddress(ipToDelete));
//Router::Instance().flushDns();
if (m_vpnConnection->connectionState() == VpnProtocol::ConnectionState::Connected) {
IpcClient::Interface()->routeDelete(ipToDelete);
IpcClient::Interface()->flushDns();
}
}
void MainWindow::initCustomSites()
void MainWindow::updateSettings()
{
customSitesModel->setStringList(m_settings.customSites());
ui->radioButton_mode_selected_sites->setChecked(m_settings.customRouting());
ui->pushButton_vpn_add_site->setEnabled(m_settings.customRouting());
ui->checkBox_autostart->setChecked(Autostart::isAutostart());
ui->checkBox_autoconnect->setChecked(m_settings.isAutoConnect());
ui->lineEdit_network_settings_dns1->setText(m_settings.primaryDns());
ui->lineEdit_network_settings_dns2->setText(m_settings.secondaryDns());
ui->listWidget_sites->clear();
for(const QString &site : m_settings.customSites()) {
makeSitesListItem(ui->listWidget_sites, site);
}
}
void MainWindow::updateShareCode()
@@ -721,4 +789,35 @@ void MainWindow::updateShareCode()
QByteArray ba = QJsonDocument(o).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
ui->textEdit_sharing_code->setText(QString("vpn://%1").arg(QString(ba)));
//qDebug() << "Share code" << QJsonDocument(o).toJson();
}
void MainWindow::makeSitesListItem(QListWidget *listWidget, const QString &address)
{
QSize size(310, 25);
QWidget* widget = new QWidget;
widget->resize(size);
QLabel *label = new QLabel(address, widget);
label->resize(size);
QPushButton* btn = new QPushButton(widget);
btn->resize(size);
QPushButton* btn1 = new QPushButton(widget);
btn1->resize(30, 25);
btn1->move(280, 0);
btn1->setCursor(QCursor(Qt::PointingHandCursor));
connect(btn1, &QPushButton::clicked, this, [this, label]() {
onPushButtonDeleteCustomSiteClicked(label->text());
return;
});
QListWidgetItem* item = new QListWidgetItem(listWidget);
item->setSizeHint(size);
listWidget->setItemWidget(item, widget);
widget->setStyleSheet(styleSheet());
}

View File

@@ -2,9 +2,11 @@
#define MAINWINDOW_H
#include <QLabel>
#include <QListWidget>
#include <QMainWindow>
#include <QProgressBar>
#include <QPushButton>
#include <QRegExpValidator>
#include <QStringListModel>
#include <QSystemTrayIcon>
@@ -35,7 +37,7 @@ public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
enum Page {Start, NewServer, Vpn, GeneralSettings, ServerSettings, ShareConnection, Sites};
enum Page {Start, NewServer, Vpn, GeneralSettings, AppSettings, NetworkSettings, ServerSettings, ShareConnection, Sites};
Q_ENUM(Page)
private slots:
@@ -52,7 +54,7 @@ private slots:
void onPushButtonForgetServer(bool);
void onPushButtonAddCustomSitesClicked();
void onPushButtonDeleteCustomSiteClicked();
void onPushButtonDeleteCustomSiteClicked(const QString &siteToDelete);
void onTrayActionConnect(); // connect from context menu
void setTrayState(VpnProtocol::ConnectionState state);
@@ -74,10 +76,10 @@ private:
void setTrayIcon(const QString &iconPath);
void setupUiConnections();
void initCustomSites();
void updateSettings();
void updateShareCode();
void makeSitesListItem(QListWidget* listWidget, const QString &address);
Ui::MainWindow *ui;
VpnConnection* m_vpnConnection;
@@ -89,7 +91,7 @@ private:
QSystemTrayIcon m_tray;
QMenu* m_menu;
QStringListModel *customSitesModel = nullptr;
QRegExpValidator m_ipAddressValidator;
bool canMove = false;
QPoint offset;

File diff suppressed because it is too large Load Diff

154
client/ui/qautostart.cpp Normal file
View File

@@ -0,0 +1,154 @@
// The MIT License (MIT)
//
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
//
// 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 "qautostart.h"
#include <QCoreApplication>
#include <QTextStream>
#include <QFileInfo>
#include <QSettings>
#include <QProcess>
#include <QString>
#include <QFile>
#include <QDir>
#if defined (Q_OS_WIN)
#define REG_KEY "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
bool Autostart::isAutostart() {
QSettings settings(REG_KEY, QSettings::NativeFormat);
if (settings.value(appName()).isNull()) {
return false;
}
return true;
}
void Autostart::setAutostart(bool autostart) {
QSettings settings(REG_KEY, QSettings::NativeFormat);
if (autostart) {
settings.setValue(appName() , appPath().replace('/','\\'));
} else {
settings.remove(appName());
}
}
QString Autostart::appPath() {
return QCoreApplication::applicationFilePath() + " --autostart";
}
#elif defined (Q_OS_MAC)
bool Autostart::isAutostart() {
QProcess process;
process.start("osascript", {
"-e tell application \"System Events\" to get the path of every login item"
});
process.waitForFinished(3000);
const auto output = QString::fromLocal8Bit(process.readAllStandardOutput());
return output.contains(appPath());
}
void Autostart::setAutostart(bool autostart) {
// Remove any existing login entry for this app first, in case there was one
// from a previous installation, that may be under a different launch path.
{
QProcess::execute("osascript", {
"-e tell application \"System Events\" to delete every login item whose name is \"" + appName() + "\""
});
}
// Now install the login item, if needed.
if ( autostart )
{
QProcess::execute("osascript", {
"-e tell application \"System Events\" to make login item at end with properties {path:\"" + appPath() + "\", hidden:true, name: \"" + appName() + "\"}"
});
}
}
QString Autostart::appPath() {
QDir appDir = QDir(QCoreApplication::applicationDirPath());
appDir.cdUp();
appDir.cdUp();
QString absolutePath = appDir.absolutePath();
return absolutePath;
}
#elif defined (Q_OS_LINUX)
bool Autostart::isAutostart() {
QFileInfo check_file(QDir::homePath() + "/.config/autostart/" + appName() +".desktop");
if (check_file.exists() && check_file.isFile()) {
return true;
}
return false;
}
void Autostart::setAutostart(bool autostart) {
QString path = QDir::homePath() + "/.config/autostart/";
QString name = appName() +".desktop";
QFile file(path+name);
file.remove();
if(autostart) {
QDir dir(path);
if(!dir.exists()) {
dir.mkpath(path);
}
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << "[Desktop Entry]" << endl;
stream << "Exec=" << appPath() << endl;
stream << "Type=Application" << endl;
}
}
}
QString Autostart::appPath() {
return QCoreApplication::applicationFilePath() + " --autostart";
}
#else
bool Autostart::isAutostart() {
return false;
}
void Autostart::setAutostart(bool autostart) {
Q_UNUSED(autostart);
}
QString Autostart::appPath() {
return QString();
}
#endif
QString Autostart::appName() {
return QCoreApplication::applicationName();
}

39
client/ui/qautostart.h Normal file
View File

@@ -0,0 +1,39 @@
// The MIT License (MIT)
//
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
//
// 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 AUTOSTART_H
#define AUTOSTART_H
#include <QString>
class Autostart
{
public:
static bool isAutostart();
static void setAutostart(bool autostart);
protected:
static QString appPath();
static QString appName();
};
#endif // AUTOSTART_H

View File

@@ -1,6 +1,8 @@
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QHostAddress>
#include <QHostInfo>
#include <QProcess>
#include <QStandardPaths>
@@ -113,45 +115,17 @@ bool Utils::processIsRunning(const QString& fileName)
QString Utils::getIPAddress(const QString& host)
{
//TODO rewrite to api calls
qDebug().noquote() << "GetIPAddress: checking " + host;
if(host.isEmpty()) {
qDebug().noquote() << "GetIPAddress: host is empty.";
return QString();
if (ipAddressRegExp().exactMatch(host)) {
return host;
}
if(checkIPFormat(host)) {
qDebug().noquote() << "GetIPAddress host is ip:" << host << host;
return host; // it is a ip address.
QList<QHostAddress> adresses = QHostInfo::fromName(host).addresses();
if (!adresses.isEmpty()) {
qDebug() << "Resolved address for" << host << adresses.first().toString();
return adresses.first().toString();
}
QProcess ping;
#ifdef Q_OS_MACX
ping.start("ping", QStringList() << "-c1" << host);
#endif
#ifdef Q_OS_WIN
ping.start("ping", QStringList() << QString("/n") << "1" << QString("/w") << "1" << host);
#endif
ping.waitForStarted();
QEventLoop loop;
loop.connect(&ping, SIGNAL(finished(int)), &loop, SLOT(quit()));
loop.exec();
QString d = ping.readAll();
if(d.size() == 0)
return QString();
qDebug().noquote() << d;
QString ip;
#ifdef Q_OS_MACX
ip = getStringBetween(d, "(", ")");
#endif
#ifdef Q_OS_WIN
ip = getStringBetween(d, "[", "]");
#endif
qDebug().noquote() << "GetIPAddress:" << host << ip;
return ip;
qDebug() << "Unable to resolve address for " << host;
return "";
}
QString Utils::getStringBetween(const QString& s, const QString& a, const QString& b)

View File

@@ -1,6 +1,7 @@
#ifndef UTILS_H
#define UTILS_H
#include <QRegExp>
#include <QString>
class Utils {
@@ -19,6 +20,7 @@ public:
static QString getIPAddress(const QString& host);
static QString getStringBetween(const QString& s, const QString& a, const QString& b);
static bool checkIPFormat(const QString& ip);
static QRegExp ipAddressRegExp() { return QRegExp("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$"); }
};
#endif // UTILS_H

View File

@@ -1,19 +1,31 @@
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QJsonObject>
#include <core/openvpnconfigurator.h>
#include <core/servercontroller.h>
#include "ipc.h"
#include "core/ipcclient.h"
#include "protocols/openvpnprotocol.h"
#include "protocols/shadowsocksvpnprotocol.h"
#include "utils.h"
#include "vpnconnection.h"
#include "communicator.h"
VpnConnection::VpnConnection(QObject* parent) : QObject(parent)
{
VpnProtocol::initializeCommunicator(parent);
QTimer::singleShot(0, this, [this](){
if (!IpcClient::init()) {
qWarning() << "Error occured when init IPC client";
emit serviceIsNotReady();
}
});
}
VpnConnection::~VpnConnection()
{
m_vpnProtocol.clear();
}
void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
@@ -23,28 +35,37 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state)
{
// if (state == VpnProtocol::ConnectionState::Connected){
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList()));
if (IpcClient::Interface()) {
if (state == VpnProtocol::ConnectionState::Connected && IpcClient::Interface()){
IpcClient::Interface()->flushDns();
// // add routes
// const QStringList &black_custom = m_settings.customIps();
// qDebug() << "onConnect :: adding custom black routes, count:" << black_custom.size();
if (m_settings.customRouting()) {
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(),
QStringList() << m_settings.primaryDns() << m_settings.secondaryDns());
const QStringList &black_custom = m_settings.customIps();
qDebug() << "onConnect :: adding custom black routes, count:" << black_custom.size();
// QStringList args;
// args << m_vpnProtocol->vpnGateway();
// args << black_custom;
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), black_custom);
}
}
else if (state == VpnProtocol::ConnectionState::Error) {
IpcClient::Interface()->flushDns();
if (m_settings.customRouting()) {
IpcClient::Interface()->clearSavedRoutes();
}
}
}
// Message m(Message::State::RoutesAddRequest, args);
// m_vpnProtocol->communicator()->sendMessage(m);
// }
// else if (state == VpnProtocol::ConnectionState::Error) {
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::ClearSavedRoutesRequest, QStringList()));
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList()));
// }
emit connectionStateChanged(state);
}
QSharedPointer<VpnProtocol> VpnConnection::vpnProtocol() const
{
return m_vpnProtocol;
}
ErrorCode VpnConnection::lastError() const
{
if (!m_vpnProtocol.data()) {
@@ -54,11 +75,12 @@ ErrorCode VpnConnection::lastError() const
return m_vpnProtocol.data()->lastError();
}
ErrorCode VpnConnection::requestVpnConfig(const ServerCredentials &credentials, Protocol protocol)
ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credentials, Protocol protocol)
{
ErrorCode errorCode = ErrorCode::NoError;
if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocks) {
QString configData = OpenVpnConfigurator::genOpenVpnConfig(credentials, protocol, &errorCode);
QString openVpnConfigData = OpenVpnConfigurator::genOpenVpnConfig(credentials, protocol, &errorCode);
m_vpnConfiguration.insert(config::key_openvpn_config_data(), openVpnConfigData);
if (errorCode) {
return errorCode;
}
@@ -66,57 +88,70 @@ ErrorCode VpnConnection::requestVpnConfig(const ServerCredentials &credentials,
QFile file(Utils::defaultVpnConfigFileName());
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
QTextStream stream(&file);
stream << configData << endl;
return ErrorCode::NoError;
stream << openVpnConfigData << endl;
}
else {
return ErrorCode::FailedToSaveConfigData;
}
return ErrorCode::FailedToSaveConfigData;
}
else {
return ErrorCode::NotImplementedError;
}
return ErrorCode::NotImplementedError;
if (protocol == Protocol::ShadowSocks) {
QJsonObject ssConfigData = ShadowSocksVpnProtocol::genShadowSocksConfig(credentials);
m_vpnConfiguration.insert(config::key_shadowsocks_config_data(), ssConfigData);
}
return ErrorCode::NoError;
}
ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Protocol protocol)
{
// protocol = Protocol::ShadowSocks;
qDebug() << "connectToVpn, CustomRouting is" << m_settings.customRouting();
protocol = Protocol::ShadowSocks;
// TODO: Try protocols one by one in case of Protocol::Any
// TODO: Implement some behavior in case if connection not stable
qDebug() << "Connect to VPN";
emit connectionStateChanged(VpnProtocol::ConnectionState::Connecting);
qApp->processEvents();
if (m_vpnProtocol) {
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
m_vpnProtocol->stop();
m_vpnProtocol.reset();
//m_vpnProtocol->deleteLater();
}
//qApp->processEvents();
if (protocol == Protocol::Any || protocol == Protocol::OpenVpn) {
ErrorCode e = requestVpnConfig(credentials, Protocol::OpenVpn);
ErrorCode e = createVpnConfiguration(credentials, Protocol::OpenVpn);
if (e) {
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
return e;
}
if (m_vpnProtocol) {
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
m_vpnProtocol.reset(new OpenVpnProtocol(m_vpnConfiguration));
e = static_cast<OpenVpnProtocol *>(m_vpnProtocol.data())->checkAndSetupTapDriver();
if (e) {
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
return e;
}
m_vpnProtocol.reset(new OpenVpnProtocol());
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
}
else if (protocol == Protocol::ShadowSocks) {
ErrorCode e = requestVpnConfig(credentials, Protocol::ShadowSocks);
ErrorCode e = createVpnConfiguration(credentials, Protocol::ShadowSocks);
if (e) {
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
return e;
}
if (m_vpnProtocol) {
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
m_vpnProtocol.reset(new ShadowSocksVpnProtocol(m_vpnConfiguration));
e = static_cast<OpenVpnProtocol *>(m_vpnProtocol.data())->checkAndSetupTapDriver();
if (e) {
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
return e;
}
m_vpnProtocol.reset(new ShadowSocksVpnProtocol(ShadowSocksVpnProtocol::genShadowSocksConfig(credentials)));
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
}
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
@@ -133,8 +168,11 @@ void VpnConnection::disconnectFromVpn()
{
qDebug() << "Disconnect from VPN";
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::ClearSavedRoutesRequest, QStringList()));
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList()));
IpcClient::Interface()->flushDns();
if (m_settings.customRouting()) {
IpcClient::Interface()->clearSavedRoutes();
}
if (!m_vpnProtocol.data()) {
return;
@@ -148,20 +186,20 @@ VpnProtocol::ConnectionState VpnConnection::connectionState()
return m_vpnProtocol->connectionState();
}
bool VpnConnection::onConnected() const
bool VpnConnection::isConnected() const
{
if (!m_vpnProtocol.data()) {
return false;
}
return m_vpnProtocol.data()->onConnected();
return m_vpnProtocol.data()->isConnected();
}
bool VpnConnection::onDisconnected() const
bool VpnConnection::isDisconnected() const
{
if (!m_vpnProtocol.data()) {
return true;
}
return m_vpnProtocol.data()->onDisconnected();
return m_vpnProtocol.data()->isDisconnected();
}

View File

@@ -4,9 +4,11 @@
#include <QObject>
#include <QString>
#include <QScopedPointer>
#include <QRemoteObjectNode>
#include "protocols/vpnprotocol.h"
#include "core/defs.h"
#include "core/ipcclient.h"
#include "settings.h"
using namespace amnezia;
@@ -17,34 +19,41 @@ class VpnConnection : public QObject
public:
explicit VpnConnection(QObject* parent = nullptr);
~VpnConnection() override = default;
~VpnConnection() override;
static QString bytesPerSecToText(quint64 bytes);
ErrorCode lastError() const;
ErrorCode requestVpnConfig(const ServerCredentials &credentials, Protocol protocol);
ErrorCode createVpnConfiguration(const ServerCredentials &credentials, Protocol protocol);
ErrorCode connectToVpn(const ServerCredentials &credentials, Protocol protocol = Protocol::Any);
bool onConnected() const;
bool onDisconnected() const;
void disconnectFromVpn();
bool isConnected() const;
bool isDisconnected() const;
VpnProtocol::ConnectionState connectionState();
QSharedPointer<VpnProtocol> vpnProtocol() const;
signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
void connectionStateChanged(VpnProtocol::ConnectionState state);
void vpnProtocolError(amnezia::ErrorCode error);
void serviceIsNotReady();
protected slots:
void onBytesChanged(quint64 receivedBytes, quint64 sentBytes);
void onConnectionStateChanged(VpnProtocol::ConnectionState state);
protected:
QScopedPointer<VpnProtocol> m_vpnProtocol;
QSharedPointer<VpnProtocol> m_vpnProtocol;
private:
Settings m_settings;
QJsonObject m_vpnConfiguration;
};
#endif // VPNCONNECTION_H

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
deploy/WWDRCA.cer Normal file

Binary file not shown.

84
deploy/build_macos.sh Normal file → Executable file
View File

@@ -5,46 +5,37 @@ set -o errexit -o nounset
# Hold on to current directory
PROJECT_DIR=$(pwd)
SCRIPT_DIR=$PROJECT_DIR/deploy
DEPLOY_DIR=$PROJECT_DIR/deploy
mkdir -p $SCRIPT_DIR/build
WORK_DIR=$SCRIPT_DIR/build
mkdir -p $DEPLOY_DIR/build
BUILD_DIR=$DEPLOY_DIR/build
echo "Project dir: ${PROJECT_DIR}"
echo "Build dir: ${WORK_DIR}"
echo "Build dir: ${BUILD_DIR}"
APP_NAME=AmneziaVPN
APP_FILENAME=$APP_NAME.app
APP_DOMAIN=org.amneziavpn.package
PLIST_NAME=$APP_NAME.plist
RELEASE_DIR=$WORK_DIR
OUT_APP_DIR=$RELEASE_DIR/client
OUT_APP_DIR=$BUILD_DIR/client
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos
INSTALLER_DATA_DIR=$RELEASE_DIR/installer/packages/$APP_DOMAIN/data
INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data
PRO_FILE_PATH=$PROJECT_DIR/$APP_NAME.pro
QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash
TARGET_FILENAME=$PROJECT_DIR/$APP_NAME.dmg
DMG_FILENAME=$PROJECT_DIR/${APP_NAME}_unsigned.dmg
# Seacrh Qt
echo "Brew Qt version $(brew --prefix qt)"
if [ -z "${QT_VERSION+x}" ]; then export QT_VERSION=5.15.2; fi
#if [ -f $(brew --prefix qt)/clang_64/bin/qmake ]; then QT_BIN_DIR=$(brew --prefix qt)/clang_64/bin;
#else QT_BIN_DIR=$HOME/Qt/5.14.2/clang_64/bin; fi
QT_BIN_DIR=$HOME/Qt/5.14.2/clang_64/bin
#QIF_BIN_DIR=$HOME/Qt/Tools/QtInstallerFramework/4.0/bin
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/clang_64/bin
QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/4.0/bin
echo "Using Qt in $QT_BIN_DIR"
echo "Using QIF in $QIF_BIN_DIR"
ls -al $QT_BIN_DIR/../../..
# Checking env
$QT_BIN_DIR/qmake -v
@@ -53,7 +44,7 @@ clang -v
# Build App
echo "Building App..."
cd $WORK_DIR
cd $BUILD_DIR
$QT_BIN_DIR/qmake $PROJECT_DIR/AmneziaVPN.pro 'CONFIG+=release CONFIG+=x86_64'
make -j `sysctl -n hw.ncpu`
@@ -67,15 +58,41 @@ echo "____________________________________"
# Package
echo "Packaging ..."
#cd $SCRIPT_DIR
#cd $DEPLOY_DIR
$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite
cp -av $RELEASE_DIR/service/server/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
cp -av $BUILD_DIR/service/server/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS
if [ "${MAC_CERT_PW+x}" ]; then
CERTIFICATE_P12=$DEPLOY_DIR/PrivacyTechAppleCertDeveloperId.p12
WWDRCA=$DEPLOY_DIR/WWDRCA.cer
KEYCHAIN=amnezia.build.keychain
TEMP_PASS=tmp_pass
security create-keychain -p $TEMP_PASS $KEYCHAIN || true
security default-keychain -s $KEYCHAIN
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
security default-keychain
security list-keychains
security import $WWDRCA -k $KEYCHAIN -T /usr/bin/codesign || true
security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MAC_CERT_PW -T /usr/bin/codesign || true
security set-key-partition-list -S apple-tool:,apple: -k $TEMP_PASS $KEYCHAIN
security find-identity -p codesigning
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $BUNDLE_DIR
/usr/bin/codesign --verify -vvvv $BUNDLE_DIR || true
spctl -a -vvvv $BUNDLE_DIR || true
fi
mkdir -p $INSTALLER_DATA_DIR
cp -av $PROJECT_DIR/deploy/installer $RELEASE_DIR
cp -av $PROJECT_DIR/deploy/installer $BUILD_DIR
cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install.sh
cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh
cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME
@@ -86,9 +103,24 @@ chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall
cd $BUNDLE_DIR
tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./
cd $RELEASE_DIR/installer
$QIF_BIN_DIR/binarycreator --offline-only -v -c config/macos.xml -p packages -f $APP_NAME
hdiutil create -volname $APP_NAME -srcfolder $APP_NAME.app -ov -format UDZO $TARGET_FILENAME
cd $BUILD_DIR/installer
$QIF_BIN_DIR/binarycreator --offline-only -v -c config/macos.xml -p packages -f $APP_FILENAME
if [ "${MAC_CERT_PW+x}" ]; then
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $APP_FILENAME
fi
hdiutil create -volname $APP_NAME -srcfolder $APP_NAME.app -ov -format UDZO $DMG_FILENAME
echo "Finished, artifact is $PROJECT_DIR/$APP_NAME.dmg"
if [ "${MAC_CERT_PW+x}" ]; then
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "Developer ID Application: Privacy Technologies OU (X7UJ388FXK)" $DMG_FILENAME
/usr/bin/codesign --verify -vvvv $DMG_FILENAME || true
spctl -a -vvvv $DMG_FILENAME || true
#xcrun altool --notarize-app -f $DMG_FILENAME -t osx --primary-bundle-id $APP_DOMAIN -u $APPLE_DEV_EMAIL
#xcrun stapler staple $DMG_FILENAME
#xcrun stapler validate $DMG_FILENAME
fi
echo "Finished, artifact is $DMG_FILENAME"
# restore keychain
security default-keychain -s login.keychain

View File

@@ -44,6 +44,12 @@ echo "PRO_FILE_PATH: %PRO_FILE_PATH%"
echo "QMAKE_STASH_FILE: %QMAKE_STASH_FILE%"
echo "TARGET_FILENAME: %TARGET_FILENAME%"
rem Signing staff
powershell Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine
powershell Get-ExecutionPolicy -List
powershell Import-PfxCertificate -FilePath %SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $(ConvertTo-SecureString -String $Env:WIN_CERT_PW -AsPlainText -Force)
echo "Cleanup..."
Rmdir /Q /S %RELEASE_DIR%
Del %QMAKE_STASH_FILE%
@@ -59,7 +65,8 @@ cd %PROJECT_DIR%
cd %WORK_DIR%
set CL=/MP
nmake /A /NOLOGO
break
if %errorlevel% neq 0 exit /b %errorlevel%
nmake clean
rem if not exist "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" break
@@ -70,11 +77,11 @@ copy "%WORK_DIR:"=%\platform\post-uninstall\release\post-uninstall.exe" %OUT_APP
echo "Signing exe"
cd %OUT_APP_DIR%
signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.exe
signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.exe
"%QT_BIN_DIR:"=%\windeployqt" --release --force --no-translations "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%"
signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.dll
signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.dll
echo "Copying deploy data..."
xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f
@@ -96,6 +103,7 @@ echo "Creating installer..."
"%QIF_BIN_DIR:"=%\binarycreator" --offline-only -v -c config\windows.xml -p packages -f %TARGET_FILENAME%
cd %PROJECT_DIR%
signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 %TARGET_FILENAME%
signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 %TARGET_FILENAME%
echo "Finished, see %TARGET_FILENAME%"
exit 0

BIN
deploy/data/macos/ss-local Executable file

Binary file not shown.

BIN
deploy/data/macos/ss-tunnel Executable file

Binary file not shown.

View File

@@ -1,101 +0,0 @@
/*
* shadowsocks.h - Header files of library interfaces
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* This file is part of the shadowsocks-libev.
* shadowsocks-libev is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* shadowsocks-libev is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with shadowsocks-libev; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef _SHADOWSOCKS_H
#define _SHADOWSOCKS_H
typedef struct {
/* Required */
char *remote_host; // hostname or ip of remote server
char *local_addr; // local ip to bind
char *method; // encryption method
char *password; // password of remote server
int remote_port; // port number of remote server
int local_port; // port number of local server
int timeout; // connection timeout
/* Optional, set NULL if not valid */
char *acl; // file path to acl
char *log; // file path to log
int fast_open; // enable tcp fast open
int mode; // enable udp relay
int mtu; // MTU of interface
int mptcp; // enable multipath TCP
int verbose; // verbose mode
} profile_t;
/* An example profile
*
* const profile_t EXAMPLE_PROFILE = {
* .remote_host = "example.com",
* .local_addr = "127.0.0.1",
* .method = "bf-cfb",
* .password = "barfoo!",
* .remote_port = 8338,
* .local_port = 1080,
* .timeout = 600;
* .acl = NULL,
* .log = NULL,
* .fast_open = 0,
* .mode = 0,
* .verbose = 0
* };
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*ss_local_callback)(int socks_fd, int udp_fd, void *data);
/*
* Create and start a shadowsocks local server.
*
* Calling this function will block the current thread forever if the server
* starts successfully.
*
* Make sure start the server in a separate process to avoid any potential
* memory and socket leak.
*
* If failed, -1 is returned. Errors will output to the log file.
*/
int start_ss_local_server(profile_t profile);
/*
* Create and start a shadowsocks local server, specifying a callback.
*
* The callback is invoked when the local server has started successfully. It passes the SOCKS
* server and UDP relay file descriptors, along with any supplied user data.
*
* Returns -1 on failure.
*/
int start_ss_local_server_with_callback(profile_t profile, ss_local_callback callback, void *udata);
#ifdef __cplusplus
}
#endif
// To stop the service on posix system, just kill the daemon process
// kill(pid, SIGKILL);
// Otherwise, If you start the service in a thread, you may need to send a signal SIGUSER1 to the thread.
// pthread_kill(pthread_t, SIGUSR1);
#endif // _SHADOWSOCKS_H

Binary file not shown.

View File

@@ -1 +1 @@
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat"
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"

30
ipc/ipc.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef IPC_H
#define IPC_H
#include <QObject>
#include <QString>
#define IPC_SERVICE_URL "local:AmneziaVpnIpcInterface"
namespace amnezia {
inline QString getIpcServiceUrl() {
#ifdef Q_OS_WIN
return IPC_SERVICE_URL;
#else
return QString("/tmp/%1").arg(IPC_SERVICE_URL);
#endif
}
inline QString getIpcProcessUrl(int pid) {
#ifdef Q_OS_WIN
return QString("%1_%2").arg(IPC_SERVICE_URL).arg(pid);
#else
return QString("/tmp/%1_%2").arg(IPC_SERVICE_URL).arg(pid);
#endif
}
} // namespace amnezia
#endif // IPC_H

43
ipc/ipcinterface.rep Normal file
View File

@@ -0,0 +1,43 @@
#include <QtCore>
#include <QString>
class IpcInterface
{
SLOT( int createPrivilegedProcess() ); // return local pid
//SIGNAL(sendMessage(const QByteArray &message));
// Route functions
SLOT( bool routeAdd(const QString &ip, const QString &gw, const QString &mask) );
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
SLOT( bool clearSavedRoutes() );
SLOT( bool routeDelete(const QString &ip) );
SLOT( void flushDns() );
SLOT( bool checkAndInstallDriver() );
SLOT( QStringList getTapList() );
};
class IpcProcessInterface
{
SLOT( start(const QString &program, const QStringList &args) );
SLOT( start() );
SLOT( close() );
SLOT( setArguments(const QStringList &arguments) );
SLOT( setInputChannelMode(QProcess::InputChannelMode mode) );
SLOT( setNativeArguments(const QString &arguments) );
SLOT( setProcessChannelMode(QProcess::ProcessChannelMode mode) );
SLOT( setProgram(const QString &program) );
SLOT( setWorkingDirectory(const QString &dir) );
SLOT( QByteArray readAllStandardError() );
SLOT( QByteArray readAllStandardOutput() );
SIGNAL( errorOccurred(QProcess::ProcessError error) );
SIGNAL( finished(int exitCode, QProcess::ExitStatus exitStatus) );
SIGNAL( readyReadStandardError() );
SIGNAL( readyReadStandardOutput() );
SIGNAL( started() );
SIGNAL( stateChanged(QProcess::ProcessState newState) );
};

97
ipc/ipcserver.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "ipcserver.h"
#include <QObject>
#include <QDateTime>
#include <QLocalSocket>
#include "router.h"
#ifdef Q_OS_WIN
#include "tapcontroller_win.h"
#endif
IpcServer::IpcServer(QObject *parent):
IpcInterfaceSource(parent)
{}
int IpcServer::createPrivilegedProcess()
{
m_localpid++;
ProcessDescriptor pd(this);
// pd.serverNode->setHostUrl(QUrl(amnezia::getIpcProcessUrl(m_localpid)));
// pd.serverNode->enableRemoting(pd.ipcProcess.data());
//pd.localServer = QSharedPointer<QLocalServer>(new QLocalServer(this));
pd.localServer->setSocketOptions(QLocalServer::WorldAccessOption);
if (!pd.localServer->listen(amnezia::getIpcProcessUrl(m_localpid))) {
qDebug() << QString("Unable to start the server: %1.").arg(pd.localServer->errorString());
return -1;
}
// connect(m_server.data(), &QLocalServer::newConnection, this, &LocalServer::onNewConnection);
// qDebug().noquote() << QString("Local server started on '%1'").arg(m_server->serverName());
// m_serverNode.setHostUrl(QUrl(QStringLiteral(IPC_SERVICE_URL))); // create host node without Registry
// Make sure any connections are handed to QtRO
QObject::connect(pd.localServer.data(), &QLocalServer::newConnection, this, [pd]() {
qDebug() << "LocalServer new connection";
if (pd.serverNode) {
pd.serverNode->addHostSideConnection(pd.localServer->nextPendingConnection());
pd.serverNode->enableRemoting(pd.ipcProcess.data());
}
});
m_processes.insert(m_localpid, pd);
return m_localpid;
}
bool IpcServer::routeAdd(const QString &ip, const QString &gw, const QString &mask)
{
return Router::routeAdd(ip, gw, mask);
}
int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
{
return Router::routeAddList(gw, ips);
}
bool IpcServer::clearSavedRoutes()
{
return Router::clearSavedRoutes();
}
bool IpcServer::routeDelete(const QString &ip)
{
return Router::routeDelete(ip);
}
void IpcServer::flushDns()
{
return Router::flushDns();
}
bool IpcServer::checkAndInstallDriver()
{
#ifdef Q_OS_WIN
return TapController::checkAndSetup();
#else
return true;
#endif
}
QStringList IpcServer::getTapList()
{
#ifdef Q_OS_WIN
return TapController::getTapList();
#else
return QStringList();
#endif
}

43
ipc/ipcserver.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef IPCSERVER_H
#define IPCSERVER_H
#include <QLocalServer>
#include <QObject>
#include "ipc.h"
#include "ipcserverprocess.h"
#include "rep_ipcinterface_source.h"
class IpcServer : public IpcInterfaceSource
{
public:
explicit IpcServer(QObject *parent = nullptr);
virtual int createPrivilegedProcess() override;
virtual bool routeAdd(const QString &ip, const QString &gw, const QString &mask = QString()) override;
virtual int routeAddList(const QString &gw, const QStringList &ips) override;
virtual bool clearSavedRoutes() override;
virtual bool routeDelete(const QString &ip) override;
virtual void flushDns() override;
virtual bool checkAndInstallDriver() override;
virtual QStringList getTapList() override;
private:
int m_localpid = 0;
struct ProcessDescriptor {
ProcessDescriptor (QObject *parent = nullptr) {
serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent));
ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent));
localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent));
}
QSharedPointer<IpcServerProcess> ipcProcess;
QSharedPointer<QRemoteObjectHost> serverNode;
QSharedPointer<QLocalServer> localServer;
};
QMap<int, ProcessDescriptor> m_processes;
};
#endif // IPCSERVER_H

97
ipc/ipcserverprocess.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "ipcserverprocess.h"
#include <QProcess>
IpcServerProcess::IpcServerProcess(QObject *parent) :
IpcProcessInterfaceSource(parent),
m_process(QSharedPointer<QProcess>(new QProcess()))
{
connect(m_process.data(), &QProcess::errorOccurred, this, &IpcServerProcess::errorOccurred);
connect(m_process.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &IpcServerProcess::finished);
connect(m_process.data(), &QProcess::readyReadStandardError, this, &IpcServerProcess::readyReadStandardError);
connect(m_process.data(), &QProcess::readyReadStandardOutput, this, &IpcServerProcess::readyReadStandardOutput);
connect(m_process.data(), &QProcess::started, this, &IpcServerProcess::started);
connect(m_process.data(), &QProcess::stateChanged, this, &IpcServerProcess::stateChanged);
connect(m_process.data(), &QProcess::errorOccurred, [&](QProcess::ProcessError error){
qDebug() << "IpcServerProcess errorOccurred " << error;
});
connect(m_process.data(), &QProcess::readyReadStandardError, [&](){
qDebug() << "IpcServerProcess StandardError " << m_process->readAllStandardError();
});
connect(m_process.data(), &QProcess::readyReadStandardOutput, [&](){
qDebug() << "IpcServerProcess StandardOutput " << m_process->readAllStandardOutput();
});
connect(m_process.data(), &QProcess::readyRead, [&](){
qDebug() << "IpcServerProcess StandardOutput " << m_process->readAll();
});
}
void IpcServerProcess::start(const QString &program, const QStringList &arguments)
{
m_process->start(program, arguments);
qDebug() << "IpcServerProcess started, " << arguments;
m_process->waitForStarted();
qDebug() << "waitForStarted started, " << m_process->errorString();
}
void IpcServerProcess::start()
{
m_process->start();
qDebug() << "IpcServerProcess started, " << m_process->program() << m_process->arguments();
m_process->waitForStarted();
qDebug() << "waitForStarted , " << m_process->errorString() << m_process->error();
}
void IpcServerProcess::close()
{
m_process->close();
}
void IpcServerProcess::setArguments(const QStringList &arguments)
{
m_process->setArguments(arguments);
}
void IpcServerProcess::setInputChannelMode(QProcess::InputChannelMode mode)
{
m_process->setInputChannelMode(mode);
}
void IpcServerProcess::setNativeArguments(const QString &arguments)
{
#ifdef Q_OS_WIN
m_process->setNativeArguments(arguments);
#endif
}
void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode)
{
m_process->setProcessChannelMode(mode);
}
void IpcServerProcess::setProgram(const QString &program)
{
m_process->setProgram(program);
}
void IpcServerProcess::setWorkingDirectory(const QString &dir)
{
m_process->setWorkingDirectory(dir);
}
QByteArray IpcServerProcess::readAllStandardError()
{
return m_process->readAllStandardError();
}
QByteArray IpcServerProcess::readAllStandardOutput()
{
return m_process->readAllStandardOutput();
}

34
ipc/ipcserverprocess.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef IPCSERVERPROCESS_H
#define IPCSERVERPROCESS_H
#include <QObject>
#include "rep_ipcinterface_source.h"
class IpcServerProcess : public IpcProcessInterfaceSource
{
Q_OBJECT
public:
explicit IpcServerProcess(QObject *parent = nullptr);
void start(const QString &program, const QStringList &arguments) override;
void start() override;
void close() override;
void setArguments(const QStringList &arguments) override;
void setInputChannelMode(QProcess::InputChannelMode mode) override;
void setNativeArguments(const QString &arguments) override;
void setProcessChannelMode(QProcess::ProcessChannelMode mode) override;
void setProgram(const QString &program) override;
void setWorkingDirectory(const QString &dir) override;
QByteArray readAllStandardError() override;
QByteArray readAllStandardOutput() override;
signals:
private:
QSharedPointer<QProcess> m_process;
};
#endif // IPCSERVERPROCESS_H

View File

@@ -3,6 +3,7 @@
#include <QLocalServer>
#include <QLocalSocket>
#include "ipc.h"
#include "localserver.h"
#include "utils.h"
@@ -13,203 +14,30 @@
#endif
LocalServer::LocalServer(QObject *parent) : QObject(parent),
m_clientConnection(nullptr),
m_clientConnected(false)
m_ipcServer(this)
{
// Create the server and listen outside of QtRO
m_server = QSharedPointer<QLocalServer>(new QLocalServer(this));
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
if (!m_server->listen(Utils::serverName())) {
if (!m_server->listen(amnezia::getIpcServiceUrl())) {
qDebug() << QString("Unable to start the server: %1.").arg(m_server->errorString());
return;
}
connect(m_server.data(), &QLocalServer::newConnection, this, &LocalServer::onNewConnection);
QObject::connect(m_server.data(), &QLocalServer::newConnection, this, [this]() {
qDebug() << "LocalServer new connection";
m_serverNode.addHostSideConnection(m_server->nextPendingConnection());
qDebug().noquote() << QString("Local server started on '%1'").arg(m_server->serverName());
if (!m_isRemotingEnabled) {
m_isRemotingEnabled = true;
m_serverNode.enableRemoting(&m_ipcServer);
}
});
}
LocalServer::~LocalServer()
{
m_clientConnected = false;
m_server->disconnect();
QFile::remove(Utils::serverName());
qDebug() << "Local server stopped";
}
bool LocalServer::isRunning() const
{
return m_server->isListening();
}
void LocalServer::onNewConnection()
{
if (m_clientConnection) {
m_clientConnection->deleteLater();
}
m_clientConnection = m_server->nextPendingConnection();
connect(m_clientConnection, &QLocalSocket::disconnected, this, &LocalServer::onDisconnected);
m_clientConnected = true;
qDebug() << "New connection";
for(;;) {
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
if (!m_clientConnected || !m_clientConnection) {
break;
}
if (m_clientConnection->waitForReadyRead(1000) && m_clientConnection->canReadLine()) {
char buf[1024];
qint64 lineLength = m_clientConnection->readLine(buf, sizeof(buf));
if (lineLength != -1) {
QString line = buf;
line = line.simplified();
qDebug().noquote() << QString("Read line: '%1'").arg(line);
Message incomingMessage(line);
if (!incomingMessage.isValid()) {
qWarning().noquote() << "Message is not valid!";
continue;
}
else {
qDebug().noquote() << QString("Got message id: '%1'").arg(static_cast<int>(incomingMessage.state()));
//qDebug().noquote() << incomingMessage.rawData();
}
switch (incomingMessage.state()) {
case Message::State::Initialize:
#ifdef Q_OS_WIN
TapController::Instance().checkAndSetup();
#endif
sendMessage(Message(Message::State::Initialize, QStringList({"Server"})));
break;
case Message::State::StartRequest:
startProcess(incomingMessage.args());
break;
case Message::State::FinishRequest:
finishProcess(incomingMessage.args());
break;
case Message::State::RoutesAddRequest:
routesAddRequest(incomingMessage.args());
break;
case Message::State::RouteDeleteRequest:
routeDeleteRequest(incomingMessage.args());
break;
case Message::State::ClearSavedRoutesRequest:
Router::Instance().clearSavedRoutes();
break;
case Message::State::FlushDnsRequest:
Router::Instance().flushDns();
break;
case Message::State::InstallDriverRequest:
checkAndInstallDriver(incomingMessage.args());
break;
default:
;
}
}
}
}
qDebug() << "Released";
}
void LocalServer::finishProcess(const QStringList& args)
{
Q_UNUSED(args)
}
void LocalServer::startProcess(const QStringList& messageArgs)
{
if (messageArgs.size() < 1) {
return;
}
QProcess* process = new QProcess();
connect(process, SIGNAL(started()), this, SLOT(onStarted()));
connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onFinished(int, QProcess::ExitStatus)));
const QString program = messageArgs.at(0);
QStringList args;
for (int i = 1; i < messageArgs.size(); i++) {
args.append(messageArgs.at(i));
}
QFileInfo fi(program);
const QString baseName = fi.baseName();
if (!fi.exists()) {
qWarning() << "This program does not exist";
sendMessage(Message(Message::State::Started, QStringList({baseName})));
sendMessage(Message(Message::State::Finished, QStringList({baseName, QString::number(-1)})));
return;
}
process->setObjectName(baseName);
qDebug().noquote() << QString("Start process '%1' - '%2' with args '%3'")
.arg(baseName).arg(program).arg(args.join(","));
process->start(program, args);
m_processList.append(process);
}
void LocalServer::routesAddRequest(const QStringList &messageArgs)
{
Router::Instance().routeAddList(messageArgs.first(), messageArgs.mid(1));
}
void LocalServer::routeDeleteRequest(const QStringList &messageArgs)
{
Router::Instance().routeDelete(messageArgs.first());
}
void LocalServer::checkAndInstallDriver(const QStringList &messageArgs)
{
}
void LocalServer::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitStatus)
QProcess* process = (QProcess*)sender();
sendMessage(Message(Message::State::Finished, QStringList({process->objectName(), QString::number(exitCode)})));
}
void LocalServer::onStarted()
{
QProcess* process = (QProcess*)sender();
sendMessage(Message(Message::State::Started, QStringList({process->objectName()})));
}
void LocalServer::onDisconnected()
{
if (!m_clientConnected) {
return;
}
m_clientConnected = false;
QLocalSocket* clientConnection = (QLocalSocket*)sender();
clientConnection->deleteLater();
qDebug() << "Diconnected";
}
void LocalServer::sendMessage(const Message& message)
{
if (!m_clientConnection || !m_clientConnected) {
qDebug()<< "Cannot send data, remote peer is not connected";
return;
}
const QString data = message.toString();
bool status = m_clientConnection->write(QString(data + "\n").toUtf8());
qDebug().noquote() << QString("Send message '%1', status '%2'").arg(data).arg(Utils::toString(status));
}

View File

@@ -8,7 +8,7 @@
#include <QStringList>
#include <QVector>
#include "message.h"
#include "ipcserver.h"
class QLocalServer;
class QLocalSocket;
@@ -22,30 +22,11 @@ public:
explicit LocalServer(QObject* parent = nullptr);
~LocalServer();
bool isRunning() const;
protected slots:
void onDisconnected();
void onNewConnection();
void onFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onStarted();
private:
void finishProcess(const QStringList& messageArgs);
void sendMessage(const Message& message);
void startProcess(const QStringList& messageArgs);
void routesAddRequest(const QStringList& messageArgs);
void routeDeleteRequest(const QStringList& messageArgs);
void checkAndInstallDriver(const QStringList& messageArgs);
QSharedPointer<QLocalServer> m_server;
QPointer <QLocalSocket> m_clientConnection;
QVector<QProcess*> m_processList;
bool m_clientConnected;
IpcServer m_ipcServer;
QRemoteObjectHost m_serverNode;
bool m_isRemotingEnabled = false;
};
#endif // LOCALSERVER_H

View File

@@ -10,9 +10,9 @@ int runApplication(int argc, char** argv)
{
QCoreApplication app(argc,argv);
LocalServer localServer;
if (!localServer.isRunning()) {
return -1;
}
// if (!localServer.isRunning()) {
// return -1;
// }
return app.exec();
}
int main(int argc, char **argv)

View File

@@ -1,327 +1,54 @@
#include "router.h"
#include <QProcess>
#ifdef Q_OS_WIN
#include "router_win.h"
#elif defined (Q_OS_MAC)
#include "router_mac.h"
#endif
Router &Router::Instance()
{
static Router s;
return s;
}
bool Router::routeAdd(const QString &ip, const QString &gw, QString mask)
{
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
.arg(ip)
.arg(mask)
.arg(gw);
#ifdef Q_OS_WIN
if (mask == "") {
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
}
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
MIB_IPFORWARDROW ipfrow;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAdd : GetBestInterface failed";
return false;
}
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
qDebug() << "Error: " << dwStatus;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
return (dwStatus == NO_ERROR);
#else
// Not implemented yet
return false;
return RouterWin::Instance().routeAdd(ip, gw, mask);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeAdd(ip, gw, mask);
#endif
}
int Router::routeAddList(const QString &gw, const QStringList &ips)
{
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
.arg(ips.size())
.arg(gw);
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
.arg(ips.join("\n"));
#ifdef Q_OS_WIN
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return 0;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return 0;
}
int success_count = 0;
QString mask;
MIB_IPFORWARDROW ipfrow;
ipfrow.dwForwardPolicy = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAddList : GetBestInterface failed";
return false;
}
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
for (int i = 0; i < ips.size(); ++i) {
QString ip = ips.at(i);
if (ip.isEmpty()) continue;
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
}
if (dwStatus == NO_ERROR) success_count++;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
return success_count;
#else
// Not implemented yet
return false;
return RouterWin::Instance().routeAddList(gw, ips);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeAddList(gw, ips);
#endif
}
bool Router::clearSavedRoutes()
{
#ifdef Q_OS_WIN
if (ipForwardRows.isEmpty()) return true;
qDebug() << "forward rows size:" << ipForwardRows.size();
// Declare and initialize variables
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
int removed_count = 0;
for (int i = 0; i < ipForwardRows.size(); ++i) {
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
}
else removed_count++;
}
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
ipForwardRows.clear();
return true;
#else
// Not implemented yet
return false;
return RouterWin::Instance().clearSavedRoutes();
#elif defined (Q_OS_MAC)
return RouterMac::Instance().clearSavedRoutes();
#endif
}
bool Router::routeDelete(const QString &ip)
{
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
#ifdef Q_OS_WIN
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("route delete %1")
.arg(ip);
p.start(command);
p.waitForFinished();
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
return true;
#else
// Not implemented yet
return false;
#endif
return RouterWin::Instance().routeDelete(ip);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeDelete(ip);
#endif
}
void Router::flushDns()
{
#ifdef Q_OS_WIN
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("ipconfig /flushdns");
p.start(command);
p.waitForFinished();
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
RouterWin::Instance().flushDns();
#elif defined (Q_OS_MAC)
RouterMac::Instance().flushDns();
#endif
}

View File

@@ -6,29 +6,7 @@
#include <QSettings>
#include <QHash>
#include <QDebug>
#ifdef Q_OS_WIN
#include <WinSock2.h> //includes Windows.h
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t u8_t ;
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif //Q_OS_WIN
#include <QObject>
/**
* @brief The Router class - General class for handling ip routing
@@ -37,24 +15,12 @@ class Router : public QObject
{
Q_OBJECT
public:
static Router& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
Router() {}
Router(Router const &) = delete;
Router& operator= (Router const&) = delete;
#ifdef Q_OS_WIN
QList<MIB_IPFORWARDROW> ipForwardRows;
#endif
static bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
static int routeAddList(const QString &gw, const QStringList &ips);
static bool clearSavedRoutes();
static bool routeDelete(const QString &ip);
static void flushDns();
};
#endif // ROUTER_H

View File

@@ -0,0 +1,69 @@
#include "router_mac.h"
#include <QProcess>
RouterMac &RouterMac::Instance()
{
static RouterMac s;
return s;
}
bool RouterMac::routeAdd(const QString &ip, const QString &gw, QString mask)
{
// route add -host ip gw
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("route", QStringList() << "add" << "-host" << ip << gw);
p.waitForFinished();
qDebug().noquote() << "OUTPUT routeAdd: " + p.readAll();
bool ok = (p.exitCode() == 0);
if (ok) {
m_addedRoutes.append(ip);
}
return ok;
}
int RouterMac::routeAddList(const QString &gw, const QStringList &ips)
{
int cnt = 0;
for (const QString &ip: ips) {
if (routeAdd(ip, gw)) cnt++;
}
return cnt;
}
bool RouterMac::clearSavedRoutes()
{
// No need to delete routes after iface down
return true;
// int cnt = 0;
// for (const QString &ip: m_addedRoutes) {
// if (routeDelete(ip)) cnt++;
// }
// return (cnt == m_addedRoutes.count());
}
bool RouterMac::routeDelete(const QString &ip)
{
// route delete ip gw
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("route", QStringList() << "delete" << ip);
p.waitForFinished();
qDebug().noquote() << "OUTPUT routeDelete: " + p.readAll();
return p.exitCode() == 0;}
void RouterMac::flushDns()
{
// sudo killall -HUP mDNSResponder
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("killall", QStringList() << "-HUP" << "mDNSResponder");
p.waitForFinished();
qDebug().noquote() << "OUTPUT killall -HUP mDNSResponder: " + p.readAll();
}

View File

@@ -0,0 +1,38 @@
#ifndef ROUTERMAC_H
#define ROUTERMAC_H
#include <QTimer>
#include <QString>
#include <QSettings>
#include <QHash>
#include <QDebug>
#include <QObject>
/**
* @brief The Router class - General class for handling ip routing
*/
class RouterMac : public QObject
{
Q_OBJECT
public:
static RouterMac& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
RouterMac() {}
RouterMac(RouterMac const &) = delete;
RouterMac& operator= (RouterMac const&) = delete;
QList<QString> m_addedRoutes;
};
#endif // ROUTERMAC_H

View File

@@ -0,0 +1,302 @@
#include "router_win.h"
#include <QProcess>
RouterWin &RouterWin::Instance()
{
static RouterWin s;
return s;
}
bool RouterWin::routeAdd(const QString &ip, const QString &gw, QString mask)
{
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
.arg(ip)
.arg(mask)
.arg(gw);
if (mask == "") {
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
}
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
MIB_IPFORWARDROW ipfrow;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAdd : GetBestInterface failed";
return false;
}
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
qDebug() << "Error: " << dwStatus;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
return (dwStatus == NO_ERROR);
}
int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
{
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
.arg(ips.size())
.arg(gw);
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
.arg(ips.join("\n"));
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return 0;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return 0;
}
int success_count = 0;
QString mask;
MIB_IPFORWARDROW ipfrow;
ipfrow.dwForwardPolicy = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAddList : GetBestInterface failed";
return false;
}
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
for (int i = 0; i < ips.size(); ++i) {
QString ip = ips.at(i);
if (ip.isEmpty()) continue;
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
}
if (dwStatus == NO_ERROR) success_count++;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
return success_count;
}
bool RouterWin::clearSavedRoutes()
{
if (ipForwardRows.isEmpty()) return true;
qDebug() << "forward rows size:" << ipForwardRows.size();
// Declare and initialize variables
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
int removed_count = 0;
for (int i = 0; i < ipForwardRows.size(); ++i) {
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
}
else removed_count++;
}
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
ipForwardRows.clear();
return true;
}
bool RouterWin::routeDelete(const QString &ip)
{
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("route delete %1")
.arg(ip);
p.start(command);
p.waitForFinished();
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
return true;
}
void RouterWin::flushDns()
{
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("ipconfig /flushdns");
p.start(command);
p.waitForFinished();
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
}

View File

@@ -0,0 +1,59 @@
#ifndef ROUTERWIN_H
#define ROUTERWIN_H
#include <QTimer>
#include <QString>
#include <QSettings>
#include <QHash>
#include <QDebug>
#include <QObject>
#ifdef Q_OS_WIN
#include <WinSock2.h> //includes Windows.h
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t u8_t ;
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif //Q_OS_WIN
/**
* @brief The Router class - General class for handling ip routing
*/
class RouterWin : public QObject
{
Q_OBJECT
public:
static RouterWin& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
RouterWin() {}
RouterWin(RouterWin const &) = delete;
RouterWin& operator= (RouterWin const&) = delete;
#ifdef Q_OS_WIN
QList<MIB_IPFORWARDROW> ipForwardRows;
#endif
};
#endif // ROUTERWIN_H

View File

@@ -1,19 +1,22 @@
TARGET = AmneziaVPN-service
TEMPLATE = app
CONFIG += console qt no_batch
QT += core network
QT += core network remoteobjects
HEADERS = \
../../client/message.h \
../../client/utils.h \
../../ipc/ipc.h \
../../ipc/ipcserver.h \
../../ipc/ipcserverprocess.h \
localserver.h \
log.h \
router.h \
systemservice.h
SOURCES = \
../../client/message.cpp \
../../client/utils.cpp \
../../ipc/ipcserver.cpp \
../../ipc/ipcserverprocess.cpp \
localserver.cpp \
log.cpp \
main.cpp \
@@ -22,10 +25,12 @@ SOURCES = \
win32 {
HEADERS += \
tapcontroller_win.h
tapcontroller_win.h \
router_win.h
SOURCES += \
tapcontroller_win.cpp
tapcontroller_win.cpp \
router_win.cpp
LIBS += \
-luser32 \
@@ -37,6 +42,14 @@ LIBS += \
-lgdi32
}
macx {
HEADERS += \
router_mac.h
SOURCES += \
router_mac.cpp
}
include(../src/qtservice.pri)
#CONFIG(release, debug|release) {
@@ -47,3 +60,5 @@ include(../src/qtservice.pri)
#}
INCLUDEPATH += "$$PWD/../../client"
REPC_SOURCE += ../../ipc/ipcinterface.rep

View File

@@ -13,9 +13,9 @@ void SystemService::start()
QCoreApplication* app = application();
m_localServer = new LocalServer();
if (!m_localServer->isRunning()) {
app->quit();
}
// if (!m_localServer->isRunning()) {
// app->quit();
// }
}
void SystemService::stop()

View File

@@ -95,6 +95,10 @@ QStringList TapController::getTapList()
else tapList.append(s);
}
if (! tapList.isEmpty()) {
enableTapAdapter(tapList.first());
}
return tapList;
}

View File

@@ -1,4 +1,4 @@
#ifndef TAPCONTROLLER_H
#ifndef TAPCONTROLLER_H
#define TAPCONTROLLER_H
#include <QObject>
@@ -35,7 +35,6 @@ private:
static bool setupDriverCertificate();
static bool removeDriver(const QString& tapInstanceId);
};
#endif // TAPCONTROLLER_H