Compare commits

...

12 Commits

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
49 changed files with 2603 additions and 846 deletions
@@ -0,0 +1,274 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <QtCore/QElapsedTimer>
#include <QtCore/QByteArray>
#include <QtCore/QSharedMemory>
#include "singleapplication.h"
#include "singleapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
* @param argc
* @param argv
* @param allowSecondary Whether to enable secondary instance support
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
{
Q_D( SingleApplication );
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
// On Android and iOS since the library is not supported fallback to
// standard QApplication behaviour by simply returning at this point.
qWarning() << "SingleApplication is not supported on Android and iOS systems.";
return;
#endif
// Store the current mode of the program
d->options = options;
// Add any unique user data
if ( ! userData.isEmpty() )
d->addAppData( userData );
// Generating an application ID used for identifying the shared memory
// block and QLocalServer
d->genBlockServerName();
// To mitigate QSharedMemory issues with large amount of processes
// attempting to attach at the same time
SingleApplicationPrivate::randomSleep();
#ifdef Q_OS_UNIX
// By explicitly attaching it and then deleting it we make sure that the
// memory is deleted even after the process has crashed on Unix.
d->memory = new QSharedMemory( d->blockServerName );
d->memory->attach();
delete d->memory;
#endif
// Guarantee thread safe behaviour with a shared memory block.
d->memory = new QSharedMemory( d->blockServerName );
// Create a shared memory block
if( d->memory->create( sizeof( InstancesInfo ) )){
// Initialize the shared memory block
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after create.";
abortSafely();
}
d->initializeMemoryBlock();
} else {
if( d->memory->error() == QSharedMemory::AlreadyExists ){
// Attempt to attach to the memory segment
if( ! d->memory->attach() ){
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
abortSafely();
}
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
abortSafely();
}
} else {
qCritical() << "SingleApplication: Unable to create block.";
abortSafely();
}
}
auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state
while( true ){
// If the shared memory block's checksum is valid continue
if( d->blockChecksum() == inst->checksum ) break;
// If more than 5s have elapsed, assume the primary instance crashed and
// assume it's position
if( time.elapsed() > 5000 ){
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
d->initializeMemoryBlock();
}
// Otherwise wait for a random period and try again. The random sleep here
// limits the probability of a collision between two racing apps and
// allows the app to initialise faster
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
qDebug() << d->memory->errorString();
}
SingleApplicationPrivate::randomSleep();
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
abortSafely();
}
}
if( inst->primary == false ){
d->startPrimary();
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
qDebug() << d->memory->errorString();
}
return;
}
// Check if another instance can be started
if( allowSecondary ){
d->startSecondary();
if( d->options & Mode::SecondaryNotification ){
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
qDebug() << d->memory->errorString();
}
return;
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
qDebug() << d->memory->errorString();
}
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
delete d;
::exit( EXIT_SUCCESS );
}
SingleApplication::~SingleApplication()
{
Q_D( SingleApplication );
delete d;
}
/**
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
bool SingleApplication::isPrimary() const
{
Q_D( const SingleApplication );
return d->server != nullptr;
}
/**
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
bool SingleApplication::isSecondary() const
{
Q_D( const SingleApplication );
return d->server == nullptr;
}
/**
* Allows you to identify an instance by returning unique consecutive instance
* ids. It is reset when the first (primary) instance of your app starts and
* only incremented afterwards.
* @return Returns a unique instance id.
*/
quint32 SingleApplication::instanceId() const
{
Q_D( const SingleApplication );
return d->instanceNumber;
}
/**
* Returns the OS PID (Process Identifier) of the process running the primary
* instance. Especially useful when SingleApplication is coupled with OS.
* specific APIs.
* @return Returns the primary instance PID.
*/
qint64 SingleApplication::primaryPid() const
{
Q_D( const SingleApplication );
return d->primaryPid();
}
/**
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
QString SingleApplication::primaryUser() const
{
Q_D( const SingleApplication );
return d->primaryUser();
}
/**
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
QString SingleApplication::currentUser() const
{
return SingleApplicationPrivate::getUsername();
}
/**
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfuly, false otherwise.
*/
bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
{
Q_D( SingleApplication );
// Nobody to connect to
if( isPrimary() ) return false;
// Make sure the socket is connected
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false;
d->socket->write( message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
}
/**
* Cleans up the shared memory block and exits with a failure.
* This function halts program execution.
*/
void SingleApplication::abortSafely()
{
Q_D( SingleApplication );
qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
delete d;
::exit( EXIT_FAILURE );
}
QStringList SingleApplication::userData() const
{
Q_D( const SingleApplication );
return d->appData();
}
@@ -0,0 +1,154 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H
#include <QtCore/QtGlobal>
#include <QtNetwork/QLocalSocket>
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QApplication
#endif
#include QT_STRINGIFY(QAPPLICATION_CLASS)
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multiple instances of the same
* Application
* @see QCoreApplication
*/
class SingleApplication : public QAPPLICATION_CLASS
{
Q_OBJECT
using app_t = QAPPLICATION_CLASS;
public:
/**
* @brief Mode of operation of SingleApplication.
* Whether the block should be user-wide or system-wide and whether the
* primary instance should be notified when a secondary instance had been
* started.
* @note Operating system can restrict the shared memory blocks to the same
* user, in which case the User/System modes will have no effect and the
* block will be user wide.
* @enum
*/
enum Mode {
User = 1 << 0,
System = 1 << 1,
SecondaryNotification = 1 << 2,
ExcludeAppVersion = 1 << 3,
ExcludeAppPath = 1 << 4
};
Q_DECLARE_FLAGS(Options, Mode)
/**
* @brief Intitializes a SingleApplication instance with argc command line
* arguments in argv
* @arg {int &} argc - Number of arguments in argv
* @arg {const char *[]} argv - Supplied command line arguments
* @arg {bool} allowSecondary - Whether to start the instance as secondary
* if there is already a primary instance.
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
* User wide or System wide.
* @arg {int} timeout - Timeout to wait in milliseconds.
* @note argc and argv may be changed as Qt removes arguments that it
* recognizes
* @note Mode::SecondaryNotification only works if set on both the primary
* instance and the secondary instance.
* @note The timeout is just a hint for the maximum time of blocking
* operations. It does not guarantee that the SingleApplication
* initialisation will be completed in given time, though is a good hint.
* Usually 4*timeout would be the worst case (fail) scenario.
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
*/
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
~SingleApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting
* @returns {bool}
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage( const QByteArray &message, int timeout = 100 );
/**
* @brief Get the set user data.
* @returns {QStringList}
*/
QStringList userData() const;
Q_SIGNALS:
void instanceStarted();
void receivedMessage( quint32 instanceId, QByteArray message );
private:
SingleApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleApplication)
void abortSafely();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
#endif // SINGLE_APPLICATION_H
@@ -0,0 +1,15 @@
QT += core network
CONFIG += c++11
HEADERS += \
$$PWD/singleapplication.h \
$$PWD/singleapplication_p.h
SOURCES += $$PWD/singleapplication.cpp \
$$PWD/singleapplication_p.cpp
INCLUDEPATH += $$PWD
win32 {
msvc:LIBS += Advapi32.lib
gcc:LIBS += -ladvapi32
}
@@ -0,0 +1,486 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <cstdlib>
#include <cstddef>
#include <QtCore/QDir>
#include <QtCore/QThread>
#include <QtCore/QByteArray>
#include <QtCore/QDataStream>
#include <QtCore/QElapsedTimer>
#include <QtCore/QCryptographicHash>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
#include <QtCore/QRandomGenerator>
#else
#include <QtCore/QDateTime>
#endif
#include "singleapplication.h"
#include "singleapplication_p.h"
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif
#ifdef Q_OS_WIN
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#include <lmcons.h>
#endif
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
: q_ptr( q_ptr )
{
server = nullptr;
socket = nullptr;
memory = nullptr;
instanceNumber = 0;
}
SingleApplicationPrivate::~SingleApplicationPrivate()
{
if( socket != nullptr ){
socket->close();
delete socket;
}
if( memory != nullptr ){
memory->lock();
auto *inst = static_cast<InstancesInfo*>(memory->data());
if( server != nullptr ){
server->close();
delete server;
inst->primary = false;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
memory->unlock();
delete memory;
}
}
QString SingleApplicationPrivate::getUsername()
{
#ifdef Q_OS_WIN
wchar_t username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = UNLEN + 1;
if( GetUserNameW( username, &usernameLength ) )
return QString::fromWCharArray( username );
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
#else
return qEnvironmentVariable( "USERNAME" );
#endif
#endif
#ifdef Q_OS_UNIX
QString username;
uid_t uid = geteuid();
struct passwd *pw = getpwuid( uid );
if( pw )
username = QString::fromLocal8Bit( pw->pw_name );
if ( username.isEmpty() ){
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
username = QString::fromLocal8Bit( qgetenv( "USER" ) );
#else
username = qEnvironmentVariable( "USER" );
#endif
}
return username;
#endif
}
void SingleApplicationPrivate::genBlockServerName()
{
QCryptographicHash appData( QCryptographicHash::Sha256 );
appData.addData( "SingleApplication", 17 );
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
if ( ! appDataList.isEmpty() )
appData.addData( appDataList.join( "" ).toUtf8() );
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
}
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
#ifdef Q_OS_WIN
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
#else
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
#endif
}
// User level block requires a user specific data in the hash
if( options & SingleApplication::Mode::User ){
appData.addData( getUsername().toUtf8() );
}
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
// server naming requirements.
blockServerName = appData.result().toBase64().replace("/", "_");
}
void SingleApplicationPrivate::initializeMemoryBlock() const
{
auto *inst = static_cast<InstancesInfo*>( memory->data() );
inst->primary = false;
inst->secondary = 0;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
void SingleApplicationPrivate::startPrimary()
{
// Reset the number of connections
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->primary = true;
inst->primaryPid = QCoreApplication::applicationPid();
qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
inst->checksum = blockChecksum();
instanceNumber = 0;
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
// Restrict access to the socket according to the
// SingleApplication::Mode::User flag on User level or no restrictions
if( options & SingleApplication::Mode::User ){
server->setSocketOptions( QLocalServer::UserAccessOption );
} else {
server->setSocketOptions( QLocalServer::WorldAccessOption );
}
server->listen( blockServerName );
QObject::connect(
server,
&QLocalServer::newConnection,
this,
&SingleApplicationPrivate::slotConnectionEstablished
);
}
void SingleApplicationPrivate::startSecondary()
{
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->secondary += 1;
inst->checksum = blockChecksum();
instanceNumber = inst->secondary;
}
bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
{
QElapsedTimer time;
time.start();
// Connect to the Local Server of the Primary Instance if not already
// connected.
if( socket == nullptr ){
socket = new QLocalSocket();
}
if( socket->state() == QLocalSocket::ConnectedState ) return true;
if( socket->state() != QLocalSocket::ConnectedState ){
while( true ){
randomSleep();
if( socket->state() != QLocalSocket::ConnectingState )
socket->connectToServer( blockServerName );
if( socket->state() == QLocalSocket::ConnectingState ){
socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
}
// If connected break out of the loop
if( socket->state() == QLocalSocket::ConnectedState ) break;
// If elapsed time since start is longer than the method timeout return
if( time.elapsed() >= msecs ) return false;
}
}
// Initialisation message according to the SingleApplication protocol
QByteArray initMsg;
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
writeStream.setVersion(QDataStream::Qt_5_6);
#endif
writeStream << blockServerName.toLatin1();
writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif
writeStream << checksum;
// The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
headerStream << static_cast <quint64>( initMsg.length() );
socket->write( header );
socket->write( initMsg );
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
socket->flush();
return result;
}
quint16 SingleApplicationPrivate::blockChecksum() const
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
#else
quint16 checksum = qChecksum(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum));
#endif
return checksum;
}
qint64 SingleApplicationPrivate::primaryPid() const
{
qint64 pid;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
pid = inst->primaryPid;
memory->unlock();
return pid;
}
QString SingleApplicationPrivate::primaryUser() const
{
QByteArray username;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
username = inst->primaryUser;
memory->unlock();
return QString::fromUtf8( username );
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished()
{
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
}
);
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
[nextConnSocket, this](){
connectionMap.remove(nextConnSocket);
}
);
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
switch(info.stage){
case StageHeader:
readInitMessageHeader(nextConnSocket);
break;
case StageBody:
readInitMessageBody(nextConnSocket);
break;
case StageConnected:
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
break;
default:
break;
};
}
);
}
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )){
return;
}
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
return;
}
QDataStream headerStream( sock );
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion( QDataStream::Qt_5_6 );
#endif
// Read the header to know the message length
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock];
info.stage = StageBody;
info.msgLen = msgLen;
if ( sock->bytesAvailable() >= (qint64) msgLen ){
readInitMessageBody( sock );
}
}
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{
Q_Q(SingleApplication);
if (!connectionMap.contains( sock )){
return;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return;
}
// Read the message body
QByteArray msgBytes = sock->read(info.msgLen);
QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
readStream.setVersion( QDataStream::Qt_5_6 );
#endif
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast <ConnectionType>( connTypeVal );
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif
bool isValid = readStream.status() == QDataStream::Ok &&
QLatin1String(latin1Name) == blockServerName &&
msgChecksum == actualChecksum;
if( !isValid ){
sock->close();
return;
}
info.instanceId = instanceId;
info.stage = StageConnected;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
options & SingleApplication::Mode::SecondaryNotification ) )
{
Q_EMIT q->instanceStarted();
}
if (sock->bytesAvailable() > 0){
Q_EMIT this->slotDataAvailable( sock, instanceId );
}
}
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{
Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
}
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId );
}
void SingleApplicationPrivate::randomSleep()
{
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
#endif
}
void SingleApplicationPrivate::addAppData(const QString &data)
{
appDataList.push_back(data);
}
QStringList SingleApplicationPrivate::appData() const
{
return appDataList;
}
@@ -0,0 +1,104 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#ifndef SINGLEAPPLICATION_P_H
#define SINGLEAPPLICATION_P_H
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include "singleapplication.h"
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
char primaryUser[128];
quint16 checksum; // Must be the last field
};
struct ConnectionInfo {
qint64 msgLen = 0;
quint32 instanceId = 0;
quint8 stage = 0;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
enum ConnectionType : quint8 {
InvalidConnection = 0,
NewInstance = 1,
SecondaryInstance = 2,
Reconnect = 3
};
enum ConnectionStage : quint8 {
StageHeader = 0,
StageBody = 1,
StageConnected = 2,
};
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr );
~SingleApplicationPrivate() override;
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary( int msecs, ConnectionType connectionType );
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket);
static void randomSleep();
void addAppData(const QString &data);
QStringList appData() const;
SingleApplication *q_ptr;
QSharedMemory *memory;
QLocalSocket *socket;
QLocalServer *server;
quint32 instanceNumber;
QString blockServerName;
SingleApplication::Options options;
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
QStringList appDataList;
public Q_SLOTS:
void slotConnectionEstablished();
void slotDataAvailable( QLocalSocket*, quint32 );
void slotClientConnectionClosed( QLocalSocket*, quint32 );
};
#endif // SINGLEAPPLICATION_P_H
+3 -2
View File
@@ -8,6 +8,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
include("3rd/QtSsh/src/ssh/ssh.pri")
include("3rd/QtSsh/src/botan/botan.pri")
include("3rd/SingleApplication/singleapplication.pri")
HEADERS += \
../ipc/ipc.h \
@@ -20,10 +21,10 @@ HEADERS += \
defines.h \
managementserver.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 \
@@ -37,10 +38,10 @@ SOURCES += \
main.cpp \
managementserver.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 \
+1
View File
@@ -56,6 +56,7 @@ enum ErrorCode
// Distro errors
OpenVpnExecutableMissing,
EasyRsaExecutableMissing,
ShadowSocksExecutableMissing,
AmneziaServiceConnectionFailed,
// VPN errors
+3
View File
@@ -237,6 +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;
}
+30 -14
View File
@@ -25,8 +25,8 @@ QString ServerController::getContainerName(DockerContainer container)
ErrorCode ServerController::runScript(DockerContainer container,
const SshConnectionParameters &sshParams, QString script,
const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr)
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdOut,
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdErr)
{
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
@@ -74,7 +74,7 @@ ErrorCode ServerController::runScript(DockerContainer container,
if (s != "." && !s.isEmpty()) {
qDebug().noquote() << "stdout" << s;
}
if (cbReadStdOut) cbReadStdOut(s);
if (cbReadStdOut) cbReadStdOut(s, proc);
});
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
@@ -82,7 +82,7 @@ ErrorCode ServerController::runScript(DockerContainer container,
if (s != "." && !s.isEmpty()) {
qDebug().noquote() << "stderr" << s;
}
if (cbReadStdErr) cbReadStdErr(s);
if (cbReadStdErr) cbReadStdErr(s, proc);
});
proc->start();
@@ -124,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;
@@ -143,7 +143,6 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
});
proc->start();
//wait.exec();
if (exitStatus < 0) {
wait.exec();
@@ -309,7 +308,8 @@ 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);
@@ -318,7 +318,7 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr
//return ErrorCode::NotImplementedError;
// TODO: run concurently
setupOpenVpnServer(credentials);
//setupOpenVpnServer(credentials);
setupShadowSocksServer(credentials);
}
@@ -336,10 +336,14 @@ ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credenti
if (scriptData.isEmpty()) return ErrorCode::InternalError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data) {
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) {
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
};
@@ -364,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
@@ -385,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);
+2 -2
View File
@@ -49,8 +49,8 @@ private:
static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams);
static ErrorCode runScript(DockerContainer container,
const QSsh::SshConnectionParameters &sshParams, QString script,
const std::function<void(const QString &)> &cbReadStdOut = nullptr,
const std::function<void(const QString &)> &cbReadStdErr = nullptr);
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

+22 -9
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();
}
+54 -37
View File
@@ -4,7 +4,6 @@
#include <QRegularExpression>
#include <QTcpSocket>
//#include "communicator.h"
#include "debug.h"
#include "openvpnprotocol.h"
#include "utils.h"
@@ -104,12 +103,13 @@ 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();
@@ -188,7 +188,6 @@ ErrorCode OpenVpnProtocol::start()
m_openVpnProcess->start();
//m_communicator->sendMessage(Message(Message::State::StartRequest, args));
//startTimeoutTimer();
return ErrorCode::NoError;
@@ -229,7 +228,6 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
if (line.contains("CONNECTED,SUCCESS")) {
sendByteCount();
stopTimeoutTimer();
updateVpnGateway();
setConnectionState(VpnProtocol::ConnectionState::Connected);
continue;
} else if (line.contains("EXITING,SIGTER")) {
@@ -246,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);
@@ -272,46 +274,61 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
}
}
void OpenVpnProtocol::updateVpnGateway()
void OpenVpnProtocol::updateVpnGateway(const QString &line)
{
QProcess ipconfig;
ipconfig.start("ipconfig", QStringList() << "/all");
ipconfig.waitForStarted();
ipconfig.waitForFinished();
// 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'
QString d = ipconfig.readAll();
d.replace("\r", "");
//qDebug().noquote() << d;
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);
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 = "";
}
+1 -1
View File
@@ -47,7 +47,7 @@ private:
private:
void updateRouteGateway(QString line);
void updateVpnGateway();
void updateVpnGateway(const QString &line);
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
};
+7 -2
View File
@@ -1,7 +1,6 @@
#include "shadowsocksvpnprotocol.h"
#include "core/servercontroller.h"
//#include "communicator.h"
#include "debug.h"
#include "utils.h"
@@ -14,6 +13,12 @@ ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration,
readShadowSocksConfiguration(configuration);
}
ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol()
{
qDebug() << "ShadowSocksVpnProtocol::stop()";
ShadowSocksVpnProtocol::stop();
}
ErrorCode ShadowSocksVpnProtocol::start()
{
qDebug() << "ShadowSocksVpnProtocol::start()";
@@ -40,7 +45,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
return OpenVpnProtocol::start();
}
else return ErrorCode::FailedToStartRemoteProcessError;
else return ErrorCode::ShadowSocksExecutableMissing;
}
void ShadowSocksVpnProtocol::stop()
@@ -8,6 +8,7 @@ class ShadowSocksVpnProtocol : public OpenVpnProtocol
{
public:
ShadowSocksVpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~ShadowSocksVpnProtocol() override;
ErrorCode start() override;
void stop() override;
+1
View File
@@ -55,6 +55,7 @@ protected:
ConnectionState m_connectionState;
QString m_routeGateway;
QString m_vpnAddress;
QString m_vpnGateway;
QJsonObject m_rawConfig;
+1
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>
-88
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();
}
-37
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
@@ -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
@@ -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
+3
View File
@@ -34,6 +34,9 @@ public:
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); }
+102 -20
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,12 +62,6 @@ 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();
});
@@ -74,7 +69,6 @@ MainWindow::MainWindow(QWidget *parent) :
updateSettings();
//ui->pushButton_general_settings_exit->hide();
//ui->pushButton_share_connection->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()
@@ -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;
@@ -263,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();
@@ -298,6 +306,7 @@ bool MainWindow::installServer(ServerCredentials credentials,
ErrorCode e = ServerController::setupServer(credentials, Protocol::Any);
qDebug() << "Setup server finished with code" << e;
if (e) {
page->setEnabled(true);
button->setVisible(true);
@@ -453,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()));
@@ -526,6 +537,8 @@ void MainWindow::setupUiConnections()
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);
@@ -536,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"));
});
});
@@ -545,17 +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)
@@ -689,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;
}
@@ -710,7 +753,6 @@ void MainWindow::onPushButtonDeleteCustomSiteClicked()
qDebug() << "Deleted custom ip:" << ipToDelete;
m_settings.setCustomIps(customIps);
updateSettings();
if (m_vpnConnection->connectionState() == VpnProtocol::ConnectionState::Connected) {
@@ -721,9 +763,20 @@ void MainWindow::onPushButtonDeleteCustomSiteClicked()
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()
@@ -739,3 +792,32 @@ void MainWindow::updateShareCode()
//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());
}
+6 -4
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);
@@ -77,7 +79,7 @@ private:
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;
+592 -141
View File
@@ -60,31 +60,26 @@ QLineEdit:disabled {
QCheckBox {
color: rgb(200, 200, 200);
color: #181922;
font: 63 11pt &quot;Open Sans&quot;;
/*font: 11pt &quot;JMH Cthulhumbus&quot;;*/
background: transparent;
}
QCheckBox::indicator {
min-height: 20px;
min-width: 20px;
border-image: url(:/images/controls/checkbox_unchecked.png) 0 0 0 0 stretch stretch;
border-image: url(:/images/controls/check_off.png) 0 0 0 0 stretch stretch;
}
QCheckBox::indicator:unchecked {
border-image: url(:/images/controls/checkbox_unchecked.png) 0 0 0 0 stretch stretch;
}
QCheckBox::indicator:unchecked:hover {
border-image: url(:/images/controls/checkbox_hover.png);
border-image: url(:/images/controls/check_off.png) 0 0 0 0 stretch stretch;
}
QCheckBox::indicator:checked {
border-image: url(:/images/controls/checkbox_checked.png);
border-image: url(:/images/controls/check_on.png);
}
@@ -259,7 +254,7 @@ QPushButton:hover {
<string notr="true"/>
</property>
<property name="currentIndex">
<number>2</number>
<number>3</number>
</property>
<widget class="QWidget" name="page_start">
<widget class="QLabel" name="label_23">
@@ -383,6 +378,10 @@ line-height: 21px;
background: #100A44;
border-radius: 4px;
}
QPushButton:hover {
background: #211966;
}
</string>
</property>
<property name="text">
@@ -634,7 +633,9 @@ line-height: 21px;
background: #100A44;
border-radius: 4px;
}
</string>
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>Connect</string>
@@ -952,6 +953,10 @@ line-height: 21px;
QPushButton:!enabled {
background: #484952;
}
QPushButton:hover {
background: #282932;
}</string>
</property>
<property name="text">
@@ -1119,9 +1124,9 @@ color: #181922;
<property name="geometry">
<rect>
<x>0</x>
<y>300</y>
<y>290</y>
<width>381</width>
<height>51</height>
<height>61</height>
</rect>
</property>
<property name="text">
@@ -1170,22 +1175,6 @@ background: rgba(167, 167, 167, 0.1);
color: #181922;
}</string>
</property>
<widget class="QListView" name="listView_sites">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>381</width>
<height>0</height>
</rect>
</property>
<property name="cursor" stdset="0">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
<widget class="QPushButton" name="pushButton_back_from_sites">
<property name="geometry">
<rect>
@@ -1243,10 +1232,10 @@ QPushButton:hover {
</property>
<property name="geometry">
<rect>
<x>19</x>
<x>20</x>
<y>50</y>
<width>341</width>
<height>41</height>
<width>340</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
@@ -1255,11 +1244,8 @@ font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
/* or 125% */
/* black */
color: #181922;</string>
color: #100A44;
</string>
</property>
<property name="text">
<string>These sites will be opened using VPN</string>
@@ -1271,22 +1257,6 @@ color: #181922;</string>
<bool>true</bool>
</property>
</widget>
<widget class="QListView" name="listView_sites_custom">
<property name="geometry">
<rect>
<x>20</x>
<y>200</y>
<width>341</width>
<height>361</height>
</rect>
</property>
<property name="cursor" stdset="0">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_sites_add_custom">
<property name="geometry">
<rect>
@@ -1338,37 +1308,20 @@ border: 1px solid #A7A7A7;
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">background: #100A44;
<string notr="true">QPushButton {
background: #100A44;
border-radius: 4px;
font-size: 24px;
color: white</string>
color: white
}
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_sites_delete_custom">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>90</x>
<y>584</y>
<width>201</width>
<height>21</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">color: #181922;</string>
</property>
<property name="text">
<string>Delete selected site</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
@@ -1394,6 +1347,49 @@ color: #333333;</string>
<string>Web site or hostname or IP address</string>
</property>
</widget>
<widget class="QListWidget" name="listWidget_sites">
<property name="geometry">
<rect>
<x>20</x>
<y>200</y>
<width>340</width>
<height>400</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QWidget {
margin: 0px;
padding: 0px;
}
QPushButton:hover {
image: url(:/images/close.png);
image-position: right center;
}
QListView {
show-decoration-selected: 1; /* make the selection span the entire width of the view */
}
QListView::item:selected:!active {
background: transparent;
border: none;
}
QListView::item:selected:active {
background: transparent;
border: none;
}
QListView::item:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE, stop: 1 #ECEEFF);
}</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_general_settings">
<widget class="QPushButton" name="pushButton_back_from_settings">
@@ -1434,7 +1430,7 @@ QPushButton:hover {
<x>10</x>
<y>40</y>
<width>360</width>
<height>1</height>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
@@ -1448,7 +1444,7 @@ QPushButton:hover {
<property name="geometry">
<rect>
<x>30</x>
<y>60</y>
<y>180</y>
<width>330</width>
<height>30</height>
</rect>
@@ -1456,6 +1452,9 @@ QPushButton:hover {
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Reinstall server, clear server</string>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
@@ -1474,16 +1473,16 @@ background-repeat: no-repeat;
background-position: left center;</string>
</property>
<property name="text">
<string>Server settings</string>
<string>Server management</string>
</property>
</widget>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>10</x>
<y>110</y>
<y>160</y>
<width>360</width>
<height>1</height>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
@@ -1500,7 +1499,7 @@ background-repeat: no-repeat;
<property name="geometry">
<rect>
<x>30</x>
<y>130</y>
<y>240</y>
<width>330</width>
<height>30</height>
</rect>
@@ -1533,9 +1532,9 @@ background-repeat: no-repeat;
<property name="geometry">
<rect>
<x>10</x>
<y>180</y>
<y>220</y>
<width>360</width>
<height>1</height>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
@@ -1551,7 +1550,7 @@ background-repeat: no-repeat;
<x>10</x>
<y>620</y>
<width>360</width>
<height>1</height>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
@@ -1568,7 +1567,7 @@ background-repeat: no-repeat;
<property name="geometry">
<rect>
<x>30</x>
<y>570</y>
<y>580</y>
<width>330</width>
<height>30</height>
</rect>
@@ -1600,9 +1599,9 @@ background-repeat: no-repeat;
<property name="geometry">
<rect>
<x>10</x>
<y>550</y>
<y>560</y>
<width>360</width>
<height>1</height>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
@@ -1612,6 +1611,463 @@ background-repeat: no-repeat;
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_app_settings">
<property name="geometry">
<rect>
<x>30</x>
<y>60</y>
<width>330</width>
<height>30</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Auto start, Auto connect</string>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
Text-align:left;
padding-left: 30px;
/* black */
color: #100A44;
background-image: url(:/images/settings.png);
background-repeat: no-repeat;
background-position: left center;</string>
</property>
<property name="text">
<string>App settings</string>
</property>
</widget>
<widget class="QLabel" name="label_19">
<property name="geometry">
<rect>
<x>10</x>
<y>280</y>
<width>360</width>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/images/line.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_21">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>360</width>
<height>10</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/images/line.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_network_settings">
<property name="geometry">
<rect>
<x>30</x>
<y>120</y>
<width>330</width>
<height>30</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Dns, Kill Switch</string>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
Text-align:left;
padding-left: 30px;
/* black */
color: #100A44;
background-image: url(:/images/settings.png);
background-repeat: no-repeat;
background-position: left center;</string>
</property>
<property name="text">
<string>Network settings</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_app_settings">
<widget class="QPushButton" name="pushButton_back_from_app_settings">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>26</width>
<height>20</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
image: url(:/images/arrow_right.png);
image-position: left;
text-align: left;
/*font: 17pt &quot;Ancient&quot;;*/
padding: 1px;
image: url(:/images/arrow_left.png);
}
QPushButton:hover {
padding: 0px;
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_22">
<property name="geometry">
<rect>
<x>110</x>
<y>590</y>
<width>150</width>
<height>22</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_autostart">
<property name="geometry">
<rect>
<x>30</x>
<y>100</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Auto start</string>
</property>
</widget>
<widget class="QLabel" name="label_27">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>340</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
color: #100A44;
</string>
</property>
<property name="text">
<string>Application Settings</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_autoconnect">
<property name="geometry">
<rect>
<x>30</x>
<y>140</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Auto connect</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_check_for_updates">
<property name="geometry">
<rect>
<x>30</x>
<y>220</y>
<width>321</width>
<height>41</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
color:rgb(212, 212, 212);
border-radius: 4px;
font-family: Lato;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 21px;
background: #100A44;
border-radius: 4px;
}
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>Check for updates</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_network_settings">
<widget class="QPushButton" name="pushButton_back_from_network_settings">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>26</width>
<height>20</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
image: url(:/images/arrow_right.png);
image-position: left;
text-align: left;
/*font: 17pt &quot;Ancient&quot;;*/
padding: 1px;
image: url(:/images/arrow_left.png);
}
QPushButton:hover {
padding: 0px;
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_26">
<property name="geometry">
<rect>
<x>110</x>
<y>590</y>
<width>150</width>
<height>22</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_network_settings_dns1">
<property name="geometry">
<rect>
<x>40</x>
<y>120</y>
<width>271</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background: #F4F4F4;
/* grey */
border: 1px solid #A7A7A7;
color: #333333;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_28">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>340</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
color: #100A44;
</string>
</property>
<property name="text">
<string>DNS Servers</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_network_settings_dns2">
<property name="geometry">
<rect>
<x>40</x>
<y>200</y>
<width>271</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background: #F4F4F4;
/* grey */
border: 1px solid #A7A7A7;
color: #333333;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_network_settings_resetdns1">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>320</x>
<y>130</y>
<width>18</width>
<height>18</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Reset to default value</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
image: url(:/images/reload.png);
padding:1px;
}
QPushButton:hover {
padding:0px;
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_network_settings_resetdns2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>320</x>
<y>210</y>
<width>18</width>
<height>18</height>
</rect>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Reset to default value</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
image: url(:/images/reload.png);
padding:1px;
}
QPushButton:hover {
padding:0px;
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_new_server_wait_info_2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>40</x>
<y>95</y>
<width>291</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Primary DNS server</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label_new_server_wait_info_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>40</x>
<y>175</y>
<width>291</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Secondray DNS server</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_server_settings">
<widget class="QLabel" name="label_server_settings_wait_info">
@@ -1633,31 +2089,6 @@ background-repeat: no-repeat;
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label_13">
<property name="geometry">
<rect>
<x>40</x>
<y>100</y>
<width>171</width>
<height>21</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 150%;
/* or 24px */
/* text */
color: #333333;</string>
</property>
<property name="text">
<string>You connected to</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_server_settings_reinstall">
<property name="geometry">
<rect>
@@ -1684,7 +2115,9 @@ line-height: 21px;
background: #100A44;
border-radius: 4px;
}
</string>
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>Reinstall Amnezia server</string>
@@ -1725,17 +2158,17 @@ QPushButton:hover {
<widget class="QLabel" name="label_16">
<property name="geometry">
<rect>
<x>10</x>
<x>20</x>
<y>40</y>
<width>361</width>
<height>71</height>
<width>340</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: bold;
font-size: 24px;
font-size: 20px;
line-height: 25px;
color: #100A44;
</string>
@@ -1836,7 +2269,9 @@ line-height: 21px;
background: #100A44;
border-radius: 4px;
}
</string>
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>Clear server from Amnezia software</string>
@@ -1868,7 +2303,9 @@ line-height: 21px;
background: #100A44;
border-radius: 4px;
}
</string>
QPushButton:hover {
background: #211966;
}</string>
</property>
<property name="text">
<string>Forget this server</string>
@@ -1877,9 +2314,9 @@ border-radius: 4px;
<widget class="QLabel" name="label_server_settings_server">
<property name="geometry">
<rect>
<x>40</x>
<y>130</y>
<width>321</width>
<x>20</x>
<y>80</y>
<width>341</width>
<height>41</height>
</rect>
</property>
@@ -1893,9 +2330,11 @@ color: #333333;</string>
<property name="text">
<string>root@yourserver.org</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<zorder>label_server_settings_wait_info</zorder>
<zorder>label_13</zorder>
<zorder>pushButton_back_from_server_settings</zorder>
<zorder>label_16</zorder>
<zorder>progressBar_server_settings_reinstall</zorder>
@@ -1949,8 +2388,8 @@ QPushButton:hover {
<rect>
<x>20</x>
<y>40</y>
<width>341</width>
<height>41</height>
<width>340</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
@@ -1959,17 +2398,14 @@ font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 25px;
/* or 125% */
/* black */
color: #181922;</string>
color: #100A44;
</string>
</property>
<property name="text">
<string>Connection string</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -2049,7 +2485,9 @@ font-size: 16px;
line-height: 21px;
}
</string>
QPushButton:hover {
background: #282932;
}</string>
</property>
<property name="text">
<string>Copy</string>
@@ -2065,21 +2503,18 @@ line-height: 21px;
</rect>
</property>
<property name="styleSheet">
<string notr="true">
font-family: Lato;
<string notr="true">font-family: Lato;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 150%;
/* or 24px */
/* grey */
color: #A7A7A7;
color: #181922;
</string>
</property>
<property name="text">
<string>Anyone who logs in with this code will have the same rights to use the VPN as you. To create a new code, change your login and / or password for connection in your server settings.</string>
<string>Anyone who logs in with this code will have the same rights to use the VPN as you. This code includes your server credentials!
Provide this code only to TRUSTED users.</string>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
@@ -2088,6 +2523,22 @@ color: #A7A7A7;
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label_29">
<property name="geometry">
<rect>
<x>110</x>
<y>590</y>
<width>150</width>
<height>22</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</widget>
</widget>
+154
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
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
+10 -36
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)
+2
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
+15 -6
View File
@@ -49,7 +49,7 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state)
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), black_custom);
}
}
else if (state == VpnProtocol::ConnectionState::Error || state == VpnProtocol::ConnectionState::Disconnected) {
else if (state == VpnProtocol::ConnectionState::Error) {
IpcClient::Interface()->flushDns();
if (m_settings.customRouting()) {
@@ -105,7 +105,7 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent
ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Protocol protocol)
{
qDebug() << "connectToVpn, CustomRouting is" << m_settings.customRouting();
//protocol = Protocol::ShadowSocks;
protocol = Protocol::ShadowSocks;
// TODO: Try protocols one by one in case of Protocol::Any
// TODO: Implement some behavior in case if connection not stable
@@ -116,10 +116,11 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot
if (m_vpnProtocol) {
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
m_vpnProtocol->stop();
m_vpnProtocol->deleteLater();
m_vpnProtocol.reset();
//m_vpnProtocol->deleteLater();
}
qApp->processEvents();
//qApp->processEvents();
if (protocol == Protocol::Any || protocol == Protocol::OpenVpn) {
ErrorCode e = createVpnConfiguration(credentials, Protocol::OpenVpn);
@@ -143,6 +144,11 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot
}
m_vpnProtocol.reset(new ShadowSocksVpnProtocol(m_vpnConfiguration));
e = static_cast<OpenVpnProtocol *>(m_vpnProtocol.data())->checkAndSetupTapDriver();
if (e) {
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
return e;
}
}
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
@@ -162,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;
+2
View File
@@ -65,6 +65,8 @@ cd %PROJECT_DIR%
cd %WORK_DIR%
set CL=/MP
nmake /A /NOLOGO
if %errorlevel% neq 0 exit /b %errorlevel%
nmake clean
rem if not exist "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" break
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-101
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.
+1 -1
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"
+5 -5
View File
@@ -55,27 +55,27 @@ int IpcServer::createPrivilegedProcess()
bool IpcServer::routeAdd(const QString &ip, const QString &gw, const QString &mask)
{
return Router::Instance().routeAdd(ip, gw, mask);
return Router::routeAdd(ip, gw, mask);
}
int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
{
return Router::Instance().routeAddList(gw, ips);
return Router::routeAddList(gw, ips);
}
bool IpcServer::clearSavedRoutes()
{
return Router::Instance().clearSavedRoutes();
return Router::clearSavedRoutes();
}
bool IpcServer::routeDelete(const QString &ip)
{
return Router::Instance().routeDelete(ip);
return Router::routeDelete(ip);
}
void IpcServer::flushDns()
{
return Router::Instance().flushDns();
return Router::flushDns();
}
bool IpcServer::checkAndInstallDriver()
+22 -295
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
}
+7 -41
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
+69
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();
}
+38
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
+302
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();
}
+59
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
+12 -2
View File
@@ -25,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 \
@@ -40,6 +42,14 @@ LIBS += \
-lgdi32
}
macx {
HEADERS += \
router_mac.h
SOURCES += \
router_mac.cpp
}
include(../src/qtservice.pri)
#CONFIG(release, debug|release) {