diff --git a/.gitignore b/.gitignore index 11ab3943..e48347c2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ macOSPackage/ AmneziaVPN.dmg AmneziaVPN.exe +deploy/build/* +winbuild.bat # Qt-es /.qmake.cache @@ -37,6 +39,4 @@ CMakeLists.txt.user* # tmp files *.*~ -# Certificates -*.p12 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..48b10e3c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,73 @@ +language: cpp + +branches: + only: + - master + - dev + +jobs: + include: + - name: MacOS + os: osx + osx_image: xcode12.2 + + script: + - | + if [ ! -f $HOME/Qt/5.14.2/clang_64/bin/qmake ]; then \ + brew install p7zip && \ + pip3 install aqtinstall requests py7zr && \ + python3 -m aqt install --outputdir $HOME/Qt 5.14.2 mac desktop clang_64 -m qtbase && \ + python3 -m aqt tool --outputdir $HOME/Qt mac tools_ifw 4.0.1 qt.tools.ifw.40; + fi + - bash deploy/build_macos.sh + + - name: Windows + os: windows + + env: + - PATH=/c/Python39:/c/Python39/Scripts:$PATH + + before_install: + - if [ ! -f /C/Qt/5.14.2/msvc2017/bin/qmake ]; then choco install python --version 3.9.1; fi + + script: + - dir "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build" + - dir "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools" + - | + if [ ! -f /C/Qt/5.14.2/msvc2017/bin/qmake ]; then \ + pip3 install aqtinstall requests py7zr && \ + python -m aqt install --outputdir /C/Qt 5.14.2 windows desktop win32_msvc2017 -m qtbase && \ + python -m aqt tool --outputdir /C/Qt windows tools_ifw 4.0.1 qt.tools.ifw.40; \ + fi + - echo 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat"' > winbuild.bat + - echo -e "\r\n" >> winbuild.bat + - echo 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsamd64_x86.bat"' >> winbuild.bat + - echo -e "\r\n" >> winbuild.bat + - echo -e "deploy\\\build_windows.bat" >> winbuild.bat + - cat winbuild.bat + - cmd //c winbuild.bat + + + +deploy: + provider: releases + api_key: $GH_TOKEN + file: + - "AmneziaVPN.exe" + - "AmneziaVPN.dmg" + + skip_cleanup: true + on: + tags: true + branch: master + +before_cache: + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then brew cleanup; fi + # Cache only .git files under "/usr/local/Homebrew" so "brew update" does not take 5min every build + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then find /usr/local/Homebrew \! -regex ".+\.git.+" -delete; fi + +cache: + directories: + - $HOME/Qt + - /C/Qt + - $HOME/Library/Caches/Homebrew \ No newline at end of file diff --git a/client/client.pro b/client/client.pro index c63c2c55..337ef80c 100644 --- a/client/client.pro +++ b/client/client.pro @@ -14,13 +14,13 @@ HEADERS += \ core/defs.h \ core/errorstrings.h \ core/openvpnconfigurator.h \ - core/router.h \ core/servercontroller.h \ debug.h \ defines.h \ localclient.h \ managementserver.h \ message.h \ + protocols/shadowsocksvpnprotocol.h \ runguard.h \ settings.h \ ui/Controls/SlidingStackedWidget.h \ @@ -33,13 +33,13 @@ HEADERS += \ SOURCES += \ communicator.cpp \ core/openvpnconfigurator.cpp \ - core/router.cpp \ core/servercontroller.cpp \ debug.cpp \ localclient.cpp \ main.cpp \ managementserver.cpp \ message.cpp \ + protocols/shadowsocksvpnprotocol.cpp \ runguard.cpp \ settings.cpp \ ui/Controls/SlidingStackedWidget.cpp \ @@ -57,12 +57,12 @@ RESOURCES += \ TRANSLATIONS = \ translations/amneziavpn_ru.ts -CONFIG(release, debug|release) { - DESTDIR = $$PWD/../../AmneziaVPN-build/client/release - MOC_DIR = $$DESTDIR - OBJECTS_DIR = $$DESTDIR - RCC_DIR = $$DESTDIR -} +#CONFIG(release, debug|release) { +# DESTDIR = $$PWD/../../AmneziaVPN-build/client/release +# MOC_DIR = $$DESTDIR +# OBJECTS_DIR = $$DESTDIR +# RCC_DIR = $$DESTDIR +#} win32 { OTHER_FILES += platform_win/vpnclient.rc @@ -98,5 +98,5 @@ macx { HEADERS += ui/macos_util.h SOURCES += ui/macos_util.mm - LIBS += -framework Cocoa + LIBS += -framework Cocoa -framework ApplicationServices -framework CoreServices -framework Foundation -framework AppKit } diff --git a/client/communicator.cpp b/client/communicator.cpp index 04a4e811..af349a0b 100644 --- a/client/communicator.cpp +++ b/client/communicator.cpp @@ -72,5 +72,8 @@ void Communicator::sendMessage(const Message& message) const QString data = message.toString(); bool status = writeData(data + "\n"); - qDebug().noquote() << QString("Send message '%1', status '%2'").arg(data).arg(Utils::toString(status)); + qDebug().noquote() << QString("Send message '%1',%2 status '%2'"). + arg(static_cast(message.state())). + arg(data). + arg(Utils::toString(status)); } diff --git a/client/core/defs.h b/client/core/defs.h index 99fc9e75..95bf8d16 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -12,6 +12,12 @@ enum class Protocol { WireGuard }; +enum class DockerContainer { + OpenVpn, + ShadowSocks, + WireGuard +}; + struct ServerCredentials { QString hostName; diff --git a/client/core/openvpnconfigurator.cpp b/client/core/openvpnconfigurator.cpp index cfb4b146..fcbfbbac 100644 --- a/client/core/openvpnconfigurator.cpp +++ b/client/core/openvpnconfigurator.cpp @@ -57,12 +57,9 @@ ErrorCode OpenVpnConfigurator::initPKI(const QString &path) { QProcess p; p.setProcessChannelMode(QProcess::MergedChannels); - p.setProcessEnvironment(prepareEnv()); #ifdef Q_OS_WIN - //p.setProgram("sh.exe"); - //p.setNativeArguments(getEasyRsaShPath() + " init-pki"); - + p.setProcessEnvironment(prepareEnv()); p.setProgram("cmd.exe"); p.setNativeArguments(QString("/C \"sh.exe %1\"").arg(getEasyRsaShPath() + " init-pki")); #else @@ -72,9 +69,9 @@ ErrorCode OpenVpnConfigurator::initPKI(const QString &path) p.setWorkingDirectory(path); -// QObject::connect(&p, &QProcess::channelReadyRead, [&](){ -// qDebug().noquote() << p.readAll(); -// }); + // QObject::connect(&p, &QProcess::channelReadyRead, [&](){ + // qDebug().noquote() << p.readAll(); + // }); p.start(); p.waitForFinished(); @@ -87,12 +84,9 @@ ErrorCode OpenVpnConfigurator::genReq(const QString &path, const QString &client { QProcess p; p.setProcessChannelMode(QProcess::MergedChannels); - p.setProcessEnvironment(prepareEnv()); #ifdef Q_OS_WIN - //p.setProgram("sh.exe"); - //p.setNativeArguments(getEasyRsaShPath() + " gen-req " + clientId + " nopass"); - + p.setProcessEnvironment(prepareEnv()); p.setProgram("cmd.exe"); p.setNativeArguments(QString("/C \"sh.exe %1\"").arg(getEasyRsaShPath() + " gen-req " + clientId + " nopass")); #else @@ -125,15 +119,16 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() connData.clientId = getRandomString(32); QTemporaryDir dir; -// if (dir.isValid()) { -// // dir.path() returns the unique directory path -// } + // if (dir.isValid()) { + // // dir.path() returns the unique directory path + // } QString path = dir.path(); initPKI(path); ErrorCode errorCode = genReq(path, connData.clientId); + Q_UNUSED(errorCode) QFile req(path + "/pki/reqs/" + connData.clientId + ".req"); req.open(QIODevice::ReadOnly); @@ -143,52 +138,85 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() key.open(QIODevice::ReadOnly); connData.privKey = key.readAll(); -// qDebug().noquote() << connData.request; -// qDebug().noquote() << connData.privKey; + // qDebug().noquote() << connData.request; + // qDebug().noquote() << connData.privKey; return connData; } -OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials, ErrorCode *errorCode) +OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials, + Protocol proto, ErrorCode *errorCode) { OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest(); connData.host = credentials.hostName; if (connData.privKey.isEmpty() || connData.request.isEmpty()) { - *errorCode = ErrorCode::EasyRsaExecutableMissing; + if (errorCode) *errorCode = ErrorCode::EasyRsaExecutableMissing; return connData; } QString reqFileName = QString("/opt/amneziavpn_data/clients/%1.req").arg(connData.clientId); - ErrorCode e = ServerController::uploadTextFileToContainer(credentials, connData.request, reqFileName); - if (e) { - *errorCode = e; + + DockerContainer container; + if (proto == Protocol::OpenVpn) container = DockerContainer::OpenVpn; + else if (proto == Protocol::ShadowSocks) container = DockerContainer::ShadowSocks; + else { + if (errorCode) *errorCode = ErrorCode::InternalError; return connData; } - ServerController::signCert(credentials, connData.clientId); - - connData.caCert = ServerController::getTextFileFromContainer(credentials, ServerController::caCertPath(), &e); - connData.clientCert = ServerController::getTextFileFromContainer(credentials, ServerController::clientCertPath() + QString("%1.crt").arg(connData.clientId), &e); + ErrorCode e = ServerController::uploadTextFileToContainer(container, credentials, connData.request, reqFileName); if (e) { - *errorCode = e; + if (errorCode) *errorCode = e; return connData; } - connData.taKey = ServerController::getTextFileFromContainer(credentials, ServerController::taKeyPath(), &e); + e = ServerController::signCert(container, credentials, connData.clientId); + if (e) { + if (errorCode) *errorCode = e; + return connData; + } + + connData.caCert = ServerController::getTextFileFromContainer(container, credentials, ServerController::caCertPath(), &e); + connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, ServerController::clientCertPath() + QString("%1.crt").arg(connData.clientId), &e); + if (e) { + if (errorCode) *errorCode = e; + return connData; + } + + connData.taKey = ServerController::getTextFileFromContainer(container, credentials, ServerController::taKeyPath(), &e); + + if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { + if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError; + } + + ServerController::setupServerFirewall(credentials); return connData; } -QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, ErrorCode *errorCode) +QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, + Protocol proto, ErrorCode *errorCode) { - QFile configTemplFile(":/server_scripts/template.ovpn"); + QFile configTemplFile; + if (proto == Protocol::OpenVpn) + configTemplFile.setFileName(":/server_scripts/template_openvpn.ovpn"); + else if (proto == Protocol::ShadowSocks) { + configTemplFile.setFileName(":/server_scripts/template_shadowsocks.ovpn"); + } + configTemplFile.open(QIODevice::ReadOnly); QString config = configTemplFile.readAll(); - ConnectionData connData = prepareOpenVpnConfig(credentials, errorCode); + ConnectionData connData = prepareOpenVpnConfig(credentials, proto, errorCode); + + if (proto == Protocol::OpenVpn) + config.replace("$PROTO", "udp"); + else if (proto == Protocol::ShadowSocks) { + config.replace("$PROTO", "tcp"); + config.replace("$LOCAL_PROXY_PORT", QString::number(ServerController::ssContainerPort())); + } - config.replace("$PROTO", "udp"); config.replace("$REMOTE_HOST", connData.host); config.replace("$REMOTE_PORT", "1194"); config.replace("$CA_CERT", connData.caCert); diff --git a/client/core/openvpnconfigurator.h b/client/core/openvpnconfigurator.h index 9f9b060b..182aa7d3 100644 --- a/client/core/openvpnconfigurator.h +++ b/client/core/openvpnconfigurator.h @@ -22,7 +22,8 @@ public: QString host; // host ip }; - static QString genOpenVpnConfig(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); + static QString genOpenVpnConfig(const ServerCredentials &credentials, Protocol proto, + ErrorCode *errorCode = nullptr); private: static QString getRandomString(int len); @@ -34,8 +35,8 @@ private: static ConnectionData createCertRequest(); - static ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); - + static ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, + Protocol proto, ErrorCode *errorCode = nullptr); }; #endif // OPENVPNCONFIGURATOR_H diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 8498ac7b..2042d2af 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -5,13 +5,25 @@ #include #include #include +#include +#include #include "sshconnectionmanager.h" using namespace QSsh; -ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, QString script) +QString ServerController::getContainerName(DockerContainer container) +{ + switch (container) { + case(DockerContainer::OpenVpn): return "amnezia-openvpn"; + case(DockerContainer::ShadowSocks): return "amnezia-shadowsocks"; + default: return ""; + } +} + +ErrorCode ServerController::runScript(DockerContainer container, + const SshConnectionParameters &sshParams, QString script) { QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false")); @@ -26,7 +38,9 @@ ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, const QStringList &lines = script.split("\n", QString::SkipEmptyParts); for (int i = 0; i < lines.count(); i++) { - const QString &line = lines.at(i); + QString line = lines.at(i); + line.replace("$CONTAINER_NAME", getContainerName(container)); + if (line.startsWith("#")) { continue; } @@ -40,11 +54,11 @@ ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, } QEventLoop wait; - int exitStatus; + int exitStatus = -1; -// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ -// qDebug() << "Command started"; -// }); + // QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ + // qDebug() << "Command started"; + // }); QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){ exitStatus = status; @@ -52,23 +66,22 @@ ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, wait.quit(); }); -// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ -// QString s = proc->readAllStandardOutput(); -// if (s != "." && !s.isEmpty()) { -// qDebug().noquote() << s; -// } -// }); + QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ + QString s = proc->readAllStandardOutput(); + if (s != "." && !s.isEmpty()) { + qDebug().noquote() << s; + } + }); -// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ -// QString s = proc->readAllStandardError(); -// if (s != "." && !s.isEmpty()) { -// qDebug().noquote() << s; -// } -// }); + QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ + QString s = proc->readAllStandardError(); + if (s != "." && !s.isEmpty()) { + qDebug().noquote() << s; + } + }); proc->start(); - - if (i < lines.count() - 1) { + if (i < lines.count() && exitStatus < 0) { wait.exec(); } @@ -81,13 +94,13 @@ ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, return ErrorCode::NoError; } -ErrorCode ServerController::uploadTextFileToContainer(const ServerCredentials &credentials, - QString &file, const QString &path) +ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, + const ServerCredentials &credentials, QString &file, const QString &path) { QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false")); - QString script = QString("docker exec -i amneziavpn sh -c \"echo \'%1\' > %2\""). - arg(file).arg(path); + QString script = QString("docker exec -i %1 sh -c \"echo \'%2\' > %3\""). + arg(getContainerName(container)).arg(file).arg(path); qDebug().noquote() << script; @@ -104,11 +117,11 @@ ErrorCode ServerController::uploadTextFileToContainer(const ServerCredentials &c } QEventLoop wait; - int exitStatus = 0; + int exitStatus = -1; -// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ -// qDebug() << "Command started"; -// }); + // QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ + // qDebug() << "Command started"; + // }); QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){ //qDebug() << "Remote process exited with status" << status; @@ -116,25 +129,29 @@ ErrorCode ServerController::uploadTextFileToContainer(const ServerCredentials &c wait.quit(); }); -// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ -// qDebug().noquote() << proc->readAllStandardOutput(); -// }); + QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ + qDebug().noquote() << proc->readAllStandardOutput(); + }); -// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ -// qDebug().noquote() << proc->readAllStandardError(); -// }); + QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ + qDebug().noquote() << proc->readAllStandardError(); + }); proc->start(); - wait.exec(); + //wait.exec(); + + if (exitStatus < 0) { + wait.exec(); + } return fromSshProcessExitStatus(exitStatus); } -QString ServerController::getTextFileFromContainer(const ServerCredentials &credentials, const QString &path, - ErrorCode *errorCode) +QString ServerController::getTextFileFromContainer(DockerContainer container, + const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode) { - QString script = QString("docker exec -i amneziavpn sh -c \"cat \'%1\'\""). - arg(path); + QString script = QString("docker exec -i %1 sh -c \"cat \'%2\'\""). + arg(getContainerName(container)).arg(path); qDebug().noquote() << "Copy file from container\n" << script; @@ -159,9 +176,18 @@ QString ServerController::getTextFileFromContainer(const ServerCredentials &cred wait.quit(); }); + QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [&](){ + qDebug() << "ServerController::getTextFileFromContainer proc started"; + exitStatus = -1; + }); + proc->start(); wait.exec(); +// if (exitStatus < 0) { +// wait.exec(); +// } + if (SshRemoteProcess::ExitStatus(exitStatus) != QSsh::SshRemoteProcess::ExitStatus::NormalExit) { if (errorCode) *errorCode = fromSshProcessExitStatus(exitStatus); } @@ -169,25 +195,28 @@ QString ServerController::getTextFileFromContainer(const ServerCredentials &cred return proc->readAllStandardOutput(); } -ErrorCode ServerController::signCert(const ServerCredentials &credentials, QString clientId) +ErrorCode ServerController::signCert(DockerContainer container, + const ServerCredentials &credentials, QString clientId) { - QString script_import = QString("docker exec -i amneziavpn bash -c \"cd /opt/amneziavpn_data && " - "easyrsa import-req /opt/amneziavpn_data/clients/%1.req %1 &>/dev/null\"") - .arg(clientId); + QString script_import = QString("docker exec -i %1 bash -c \"cd /opt/amneziavpn_data && " + "easyrsa import-req /opt/amneziavpn_data/clients/%2.req %2\"") + .arg(getContainerName(container)).arg(clientId); - QString script_sign = QString("docker exec -i amneziavpn bash -c \"export EASYRSA_BATCH=1; cd /opt/amneziavpn_data && " - "easyrsa sign-req client %1 &>/dev/null\"") - .arg(clientId); + QString script_sign = QString("docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amneziavpn_data && " + "easyrsa sign-req client %2\"") + .arg(getContainerName(container)).arg(clientId); QStringList script {script_import, script_sign}; - return runScript(sshParams(credentials), script.join("\n")); + return runScript(container, sshParams(credentials), script.join("\n")); } -ErrorCode ServerController::checkOpenVpnServer(const ServerCredentials &credentials) +ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials) { - QString caCert = ServerController::getTextFileFromContainer(credentials, ServerController::caCertPath()); - QString taKey = ServerController::getTextFileFromContainer(credentials, ServerController::taKeyPath()); + QString caCert = ServerController::getTextFileFromContainer(container, + credentials, ServerController::caCertPath()); + QString taKey = ServerController::getTextFileFromContainer(container, + credentials, ServerController::taKeyPath()); if (!caCert.isEmpty() && !taKey.isEmpty()) { return ErrorCode::NoError; @@ -209,15 +238,18 @@ ErrorCode ServerController::fromSshConnectionErrorCode(SshError error) case(QSsh::SshAuthenticationError): return ErrorCode::SshAuthenticationError; case(QSsh::SshClosedByServerError): return ErrorCode::SshClosedByServerError; case(QSsh::SshInternalError): return ErrorCode::SshInternalError; + default: return ErrorCode::SshInternalError; } } ErrorCode ServerController::fromSshProcessExitStatus(int exitStatus) { + qDebug() << exitStatus; switch (SshRemoteProcess::ExitStatus(exitStatus)) { case(SshRemoteProcess::ExitStatus::NormalExit): return ErrorCode::NoError; case(SshRemoteProcess::ExitStatus::FailedToStart): return ErrorCode::FailedToStartRemoteProcessError; case(SshRemoteProcess::ExitStatus::CrashExit): return ErrorCode::RemoteProcessCrashError; + default: return ErrorCode::SshInternalError; } } @@ -238,10 +270,24 @@ SshConnectionParameters ServerController::sshParams(const ServerCredentials &cre ErrorCode ServerController::removeServer(const ServerCredentials &credentials, Protocol proto) { QString scriptFileName; + DockerContainer container; - if (proto == Protocol::OpenVpn || proto == Protocol::Any) { - scriptFileName = ":/server_scripts/remove_openvpn_server.sh"; + ErrorCode errorCode; + if (proto == Protocol::Any) { + removeServer(credentials, Protocol::OpenVpn); + removeServer(credentials, Protocol::ShadowSocks); + return ErrorCode::NoError; } + else if (proto == Protocol::OpenVpn) { + scriptFileName = ":/server_scripts/remove_container.sh"; + container = DockerContainer::OpenVpn; + } + else if (proto == Protocol::ShadowSocks) { + scriptFileName = ":/server_scripts/remove_container.sh"; + container = DockerContainer::ShadowSocks; + } + else return ErrorCode::NotImplementedError; + QString scriptData; @@ -251,7 +297,7 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P scriptData = file.readAll(); if (scriptData.isEmpty()) return ErrorCode::InternalError; - return runScript(sshParams(credentials), scriptData); + return runScript(container, sshParams(credentials), scriptData); } ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Protocol proto) @@ -263,8 +309,10 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr return setupShadowSocksServer(credentials); } else if (proto == Protocol::Any) { + return ErrorCode::NotImplementedError; + // TODO: run concurently - return setupOpenVpnServer(credentials); + // return setupOpenVpnServer(credentials); //setupShadowSocksServer(credentials); } @@ -281,16 +329,49 @@ ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credenti scriptData = file.readAll(); if (scriptData.isEmpty()) return ErrorCode::InternalError; - ErrorCode e = runScript(sshParams(credentials), scriptData); + ErrorCode e = runScript(DockerContainer::OpenVpn, sshParams(credentials), scriptData); if (e) return e; - //return ok; - return checkOpenVpnServer(credentials); + return checkOpenVpnServer(DockerContainer::OpenVpn, credentials); } ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &credentials) { - return ErrorCode::NotImplementedError; + // Setup openvpn part + QString scriptData; + QString scriptFileName = ":/server_scripts/setup_shadowsocks_server.sh"; + QFile file(scriptFileName); + if (! file.open(QIODevice::ReadOnly)) return ErrorCode::InternalError; + + scriptData = file.readAll(); + if (scriptData.isEmpty()) return ErrorCode::InternalError; + + ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData); + if (e) return e; + + // Create ss config + QJsonObject ssConfig; + ssConfig.insert("server", "0.0.0.0"); + ssConfig.insert("server_port", ssRemotePort()); + ssConfig.insert("local_port", ssContainerPort()); + ssConfig.insert("password", credentials.password); + ssConfig.insert("timeout", 60); + ssConfig.insert("method", ssEncryption()); + QString configData = QJsonDocument(ssConfig).toJson(); + QString sSConfigPath = "/opt/amneziavpn_data/ssConfig.json"; + + qDebug().noquote() << configData; + configData.replace("\"", "\\\""); + qDebug().noquote() << configData; + + uploadTextFileToContainer(DockerContainer::ShadowSocks, credentials, configData, sSConfigPath); + + // Start ss + QString script = QString("docker exec -i %1 sh -c \"ss-server -c %2 &\""). + arg(getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath); + + e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), script); + return e; } SshConnection *ServerController::connectToHost(const SshConnectionParameters &sshParams) @@ -314,9 +395,9 @@ SshConnection *ServerController::connectToHost(const SshConnectionParameters &ss }); -// QObject::connect(client, &SshConnection::dataAvailable, [&](const QString &message) { -// qCritical() << "Ssh message:" << message; -// }); + // QObject::connect(client, &SshConnection::dataAvailable, [&](const QString &message) { + // qCritical() << "Ssh message:" << message; + // }); //qDebug() << "Connection state" << client->state(); @@ -326,22 +407,31 @@ SshConnection *ServerController::connectToHost(const SshConnectionParameters &ss } -// QObject::connect(&client, &SshClient::sshDataReceived, [&](){ -// qDebug().noquote() << "Data received"; -// }); + // QObject::connect(&client, &SshClient::sshDataReceived, [&](){ + // qDebug().noquote() << "Data received"; + // }); -// if(client.sshState() != SshClient::SshState::Ready) { -// qCritical() << "Can't connect to server"; -// return false; -// } -// else { -// qDebug() << "SSh connection established"; -// } + // if(client.sshState() != SshClient::SshState::Ready) { + // qCritical() << "Can't connect to server"; + // return false; + // } + // else { + // qDebug() << "SSh connection established"; + // } -// QObject::connect(proc, &SshProcess::finished, &wait, &QEventLoop::quit); -// QObject::connect(proc, &SshProcess::failed, &wait, &QEventLoop::quit); + // QObject::connect(proc, &SshProcess::finished, &wait, &QEventLoop::quit); + // QObject::connect(proc, &SshProcess::failed, &wait, &QEventLoop::quit); return client; } + +ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) +{ + QFile file(":/server_scripts/setup_firewall.sh"); + file.open(QIODevice::ReadOnly); + + QString script = file.readAll(); + return runScript(DockerContainer::OpenVpn, sshParams(credentials), script); +} diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index f4458791..56289b21 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -22,21 +22,33 @@ public: static QString clientCertPath() { return "/opt/amneziavpn_data/pki/issued/"; } static QString taKeyPath() { return "/opt/amneziavpn_data/ta.key"; } + static QString getContainerName(amnezia::DockerContainer container); + static QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); static ErrorCode removeServer(const ServerCredentials &credentials, Protocol proto); static ErrorCode setupServer(const ServerCredentials &credentials, Protocol proto); - static ErrorCode checkOpenVpnServer(const ServerCredentials &credentials); + static ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); - static ErrorCode uploadTextFileToContainer(const ServerCredentials &credentials, QString &file, const QString &path); - static QString getTextFileFromContainer(const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); + static ErrorCode uploadTextFileToContainer(DockerContainer container, + const ServerCredentials &credentials, QString &file, const QString &path); - static ErrorCode signCert(const ServerCredentials &credentials, QString clientId); + static QString getTextFileFromContainer(DockerContainer container, + const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); + static ErrorCode signCert(DockerContainer container, + const ServerCredentials &credentials, QString clientId); + + static int ssRemotePort() { return 6789; } // TODO move to ShadowSocksDefs.h + static int ssContainerPort() { return 8585; } // TODO move to ShadowSocksDefs.h + static QString ssEncryption() { return "chacha20-ietf-poly1305"; } // TODO move to ShadowSocksDefs.h + + static ErrorCode setupServerFirewall(const ServerCredentials &credentials); private: static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); - static ErrorCode runScript(const QSsh::SshConnectionParameters &sshParams, QString script); + static ErrorCode runScript(DockerContainer container, + const QSsh::SshConnectionParameters &sshParams, QString script); static ErrorCode setupOpenVpnServer(const ServerCredentials &credentials); static ErrorCode setupShadowSocksServer(const ServerCredentials &credentials); diff --git a/client/images/app.icns b/client/images/app.icns index 17d67ff2..4c7670c7 100644 Binary files a/client/images/app.icns and b/client/images/app.icns differ diff --git a/client/images/app.ico b/client/images/app.ico index e64d04d5..28528b98 100644 Binary files a/client/images/app.ico and b/client/images/app.ico differ diff --git a/client/images/icon.png b/client/images/icon.png index b7d5d423..b82997bb 100644 Binary files a/client/images/icon.png and b/client/images/icon.png differ diff --git a/client/images/icon_src.png b/client/images/icon_src.png new file mode 100644 index 00000000..1201a89a Binary files /dev/null and b/client/images/icon_src.png differ diff --git a/client/images/tray/active.png b/client/images/tray/active.png index 632ee3a2..7ceac4b4 100644 Binary files a/client/images/tray/active.png and b/client/images/tray/active.png differ diff --git a/client/images/tray/default.png b/client/images/tray/default.png index 1e7dfd39..254ac14a 100644 Binary files a/client/images/tray/default.png and b/client/images/tray/default.png differ diff --git a/client/images/tray/error.png b/client/images/tray/error.png index 56efcce1..a24a32df 100644 Binary files a/client/images/tray/error.png and b/client/images/tray/error.png differ diff --git a/client/message.cpp b/client/message.cpp index eb23cda4..189c0160 100644 --- a/client/message.cpp +++ b/client/message.cpp @@ -22,12 +22,22 @@ QString Message::textState() const case State::Started: return "Started"; case State::FinishRequest: return "FinishRequest"; case State::Finished: return "Finished"; + case State::RoutesAddRequest: return "RoutesAddRequest"; + case State::RouteDeleteRequest: return "RouteDeleteRequest"; + case State::ClearSavedRoutesRequest: return "ClearSavedRoutesRequest"; + case State::FlushDnsRequest: return "FlushDnsRequest"; + case State::InstallDriverRequest: return "InstallDriverRequest"; default: ; } return QString(); } +QString Message::rawData() const +{ + return m_rawData; +} + Message::State Message::state() const { return m_state; @@ -66,6 +76,7 @@ QString Message::argsToString() const Message::Message(const QString& data) { + m_rawData = data; m_valid = false; if (data.isEmpty()) { return; @@ -77,7 +88,7 @@ Message::Message(const QString& data) } bool stateFound = false; - for (int i = static_cast(State::Unknown); i <= static_cast(State::Finished); i++ ) { + for (int i = static_cast(State::Unknown); i <= static_cast(State::InstallDriverRequest); i++ ) { m_state = static_cast(i); if (textState() == dataList.at(0)) { stateFound = true; diff --git a/client/message.h b/client/message.h index 46eed2a5..ea02c656 100644 --- a/client/message.h +++ b/client/message.h @@ -6,7 +6,8 @@ class Message { public: - enum class State {Unknown, Initialize, StartRequest, Started, FinishRequest, Finished}; + enum class State {Unknown, Initialize, StartRequest, Started, FinishRequest, Finished, + RoutesAddRequest, RouteDeleteRequest, ClearSavedRoutesRequest, FlushDnsRequest, InstallDriverRequest}; Message(State state, const QStringList& args); Message(const QString& data); @@ -16,6 +17,7 @@ public: QStringList args() const; State state() const; bool isValid() const; + QString rawData() const; protected: QString textState() const; @@ -26,6 +28,7 @@ protected: bool m_valid; State m_state; QStringList m_args; + QString m_rawData; }; #endif // MESSAGE_H diff --git a/client/platform_win/vpnclient.rc b/client/platform_win/vpnclient.rc index 7b0155b0..16f1f4d7 100644 --- a/client/platform_win/vpnclient.rc +++ b/client/platform_win/vpnclient.rc @@ -18,7 +18,7 @@ IDI_ICON1 ICON "../images/app.ico" #define VER_ORIGINALFILENAME_STR "amneziavpn.exe" #define VER_PRODUCTNAME_STR "AmneziaVPN" -#define VER_COMPANYDOMAIN_STR "http://amnezia.org/" +#define VER_COMPANYDOMAIN_STR "https://amnezia.org/" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 38f8aba5..37d1b77e 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "communicator.h" @@ -19,7 +21,8 @@ OpenVpnProtocol::OpenVpnProtocol(const QString& args, QObject* parent) : OpenVpnProtocol::~OpenVpnProtocol() { - stop(); + qDebug() << "OpenVpnProtocol::stop()"; + OpenVpnProtocol::stop(); } void OpenVpnProtocol::onMessageReceived(const Message& message) @@ -105,6 +108,15 @@ void OpenVpnProtocol::writeCommand(const QString& command) } } +void OpenVpnProtocol::updateRouteGateway(QString line) +{ + line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1); + if (!line.contains("/")) return; + m_routeGateway = line.split("/", QString::SkipEmptyParts).first(); + m_routeGateway.replace(" ", ""); + qDebug() << "Set VPN route gateway" << m_routeGateway; +} + QString OpenVpnProtocol::openVpnExecPath() const { #ifdef Q_OS_WIN @@ -120,7 +132,7 @@ ErrorCode OpenVpnProtocol::start() m_requestFromUserToStop = false; m_openVpnStateSigTermHandlerTimer.stop(); - stop(); + OpenVpnProtocol::stop(); if (communicator() && !communicator()->isConnected()) { setLastError(ErrorCode::AmneziaServiceConnectionFailed); @@ -208,6 +220,7 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer() if (line.contains("CONNECTED,SUCCESS")) { sendByteCount(); stopTimeoutTimer(); + updateVpnGateway(); setConnectionState(VpnProtocol::ConnectionState::Connected); continue; } else if (line.contains("EXITING,SIGTER")) { @@ -219,6 +232,10 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer() } } + if (line.contains("ROUTE_GATEWAY")) { + updateRouteGateway(line); + } + if (line.contains("FATAL")) { if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) { emit protocolError(ErrorCode::OpenVpnAdaptersInUseError); @@ -226,6 +243,7 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer() else { emit protocolError(ErrorCode::OpenVpnUnknownError); } + return; } QByteArray data(line.toStdString().c_str()); @@ -257,4 +275,46 @@ void OpenVpnProtocol::onOpenVpnProcessFinished(int exitCode) setConnectionState(VpnProtocol::ConnectionState::Disconnected); } +void OpenVpnProtocol::updateVpnGateway() +{ + QProcess ipconfig; + ipconfig.start("ipconfig", QStringList() << "/all"); + ipconfig.waitForStarted(); + ipconfig.waitForFinished(); + QString d = ipconfig.readAll(); + d.replace("\r", ""); + //qDebug().noquote() << d; + + QStringList adapters = d.split(":\n"); + + bool isTapV9Present = false; + QString tapV9; + for (int i = 0; i < adapters.size(); ++i) { + if (adapters.at(i).contains("TAP-Windows Adapter V9")) { + isTapV9Present = true; + tapV9 = adapters.at(i); + break; + } + } + 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 = ""; +} diff --git a/client/protocols/openvpnprotocol.h b/client/protocols/openvpnprotocol.h index 16fe8bdc..3fe8b21e 100644 --- a/client/protocols/openvpnprotocol.h +++ b/client/protocols/openvpnprotocol.h @@ -15,7 +15,7 @@ class OpenVpnProtocol : public VpnProtocol public: explicit OpenVpnProtocol(const QString& args = QString(), QObject* parent = nullptr); - ~OpenVpnProtocol() override; + virtual ~OpenVpnProtocol() override; ErrorCode start() override; void stop() override; @@ -46,6 +46,11 @@ protected: QString m_configFileName; QTimer m_openVpnStateSigTermHandlerTimer; bool m_requestFromUserToStop; + + +private: + void updateRouteGateway(QString line); + void updateVpnGateway(); }; #endif // OPENVPNPROTOCOL_H diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp new file mode 100644 index 00000000..80b164a1 --- /dev/null +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -0,0 +1,68 @@ +#include "shadowsocksvpnprotocol.h" +#include "core/servercontroller.h" + +#include "communicator.h" +#include "debug.h" +#include "utils.h" + +#include +#include + +ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QString &args, QObject *parent): + OpenVpnProtocol(args, parent) +{ + m_shadowSocksConfig = args; +} + +ErrorCode ShadowSocksVpnProtocol::start() +{ + qDebug() << "ShadowSocksVpnProtocol::start()"; + QJsonObject config = QJsonDocument::fromJson(m_shadowSocksConfig.toUtf8()).object(); + + ssProcess.setProcessChannelMode(QProcess::MergedChannels); + + ssProcess.setProgram(shadowSocksExecPath()); + ssProcess.setArguments(QStringList() << "-s" << config.value("server").toString() + << "-p" << QString::number(config.value("server_port").toInt()) + << "-l" << QString::number(config.value("local_port").toInt()) + << "-m" << config.value("method").toString() + << "-k" << config.value("password").toString() + ); + + ssProcess.start(); + ssProcess.waitForStarted(); + + if (ssProcess.state() == QProcess::ProcessState::Running) { + setConnectionState(ConnectionState::Connecting); + + return OpenVpnProtocol::start(); + } + else return ErrorCode::FailedToStartRemoteProcessError; +} + +void ShadowSocksVpnProtocol::stop() +{ + qDebug() << "ShadowSocksVpnProtocol::stop()"; + ssProcess.kill(); +} + +QString ShadowSocksVpnProtocol::shadowSocksExecPath() const +{ +#ifdef Q_OS_WIN + return Utils::executable(QString("ss/ss-local"), true); +#else + return Utils::executable(QString("/ss-local"), true); +#endif +} + +QString ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto) +{ + QJsonObject ssConfig; + ssConfig.insert("server", credentials.hostName); + ssConfig.insert("server_port", ServerController::ssRemotePort()); + ssConfig.insert("local_port", ServerController::ssContainerPort()); + ssConfig.insert("password", credentials.password); + ssConfig.insert("timeout", 60); + ssConfig.insert("method", ServerController::ssEncryption()); + return QJsonDocument(ssConfig).toJson(); +} diff --git a/client/protocols/shadowsocksvpnprotocol.h b/client/protocols/shadowsocksvpnprotocol.h new file mode 100644 index 00000000..b6645ea1 --- /dev/null +++ b/client/protocols/shadowsocksvpnprotocol.h @@ -0,0 +1,27 @@ +#ifndef SHADOWSOCKSVPNPROTOCOL_H +#define SHADOWSOCKSVPNPROTOCOL_H + +#include "openvpnprotocol.h" +#include "QProcess" + +class ShadowSocksVpnProtocol : public OpenVpnProtocol +{ +public: + ShadowSocksVpnProtocol(const QString& args = QString(), QObject* parent = nullptr); + + ErrorCode start() override; + void stop() override; + + static QString genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto = Protocol::ShadowSocks); + +protected: + QString shadowSocksExecPath() const; + +protected: + QString m_shadowSocksConfig; + +private: + QProcess ssProcess; +}; + +#endif // SHADOWSOCKSVPNPROTOCOL_H diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index fc9cbc79..8119e67e 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -94,6 +94,16 @@ void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state) emit connectionStateChanged(m_connectionState); } +QString VpnProtocol::vpnGateway() const +{ + return m_vpnGateway; +} + +QString VpnProtocol::routeGateway() const +{ + return m_routeGateway; +} + QString VpnProtocol::textConnectionState(ConnectionState connectionState) { switch (connectionState) { diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h index fa1fc9a9..a9cc1331 100644 --- a/client/protocols/vpnprotocol.h +++ b/client/protocols/vpnprotocol.h @@ -35,6 +35,9 @@ public: QString textConnectionState() const; void setLastError(ErrorCode lastError); + QString routeGateway() const; + QString vpnGateway() const; + signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); @@ -54,12 +57,15 @@ protected: static Communicator* m_communicator; ConnectionState m_connectionState; + QString m_routeGateway; + QString m_vpnGateway; private: QTimer* m_timeoutTimer; ErrorCode m_lastError; quint64 m_receivedBytes; quint64 m_sentBytes; + }; #endif // VPNPROTOCOL_H diff --git a/client/resources.qrc b/client/resources.qrc index ec0b46a2..b20355a1 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -33,10 +33,12 @@ images/line.png images/server_settings.png images/share.png - server_scripts/remove_openvpn_server.sh + server_scripts/remove_container.sh server_scripts/setup_openvpn_server.sh - server_scripts/template.ovpn + server_scripts/template_openvpn.ovpn images/background_connected.png server_scripts/setup_shadowsocks_server.sh + server_scripts/template_shadowsocks.ovpn + server_scripts/setup_firewall.sh diff --git a/client/server_scripts/remove_container.sh b/client/server_scripts/remove_container.sh new file mode 100644 index 00000000..e5dbd439 --- /dev/null +++ b/client/server_scripts/remove_container.sh @@ -0,0 +1,2 @@ +docker stop $CONTAINER_NAME +docker rm -f $CONTAINER_NAME diff --git a/client/server_scripts/remove_openvpn_server.sh b/client/server_scripts/remove_openvpn_server.sh deleted file mode 100644 index 3df28e07..00000000 --- a/client/server_scripts/remove_openvpn_server.sh +++ /dev/null @@ -1,2 +0,0 @@ -sudo docker stop amneziavpn -sudo docker rm -f amneziavpn diff --git a/client/server_scripts/setup_firewall.sh b/client/server_scripts/setup_firewall.sh new file mode 100644 index 00000000..706a7a44 --- /dev/null +++ b/client/server_scripts/setup_firewall.sh @@ -0,0 +1,3 @@ +sysctl -w net.ipv4.ip_forward=1 +iptables -P FORWARD ACCEPT +iptables -A INPUT -p icmp --icmp-type echo-request -j DROP diff --git a/client/server_scripts/setup_openvpn_server.sh b/client/server_scripts/setup_openvpn_server.sh index d9b30a15..c9714ac3 100644 --- a/client/server_scripts/setup_openvpn_server.sh +++ b/client/server_scripts/setup_openvpn_server.sh @@ -1,24 +1,24 @@ -#DOCKER_IMAGE="amneziavpn/openvpn:latest" -#CONTAINER_NAME="amneziavpn" +#CONTAINER_NAME=... this var will be set in ServerController -#sudo apt update -sudo apt install -y docker.io curl -sudo systemctl start docker +apt-get update -sudo docker stop amneziavpn -sudo docker rm -f amneziavpn -sudo docker pull amneziavpn/openvpn:latest -sudo docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/udp --name amneziavpn amneziavpn/openvpn:latest +iptables -P FORWARD ACCEPT + +apt install -y docker.io curl +systemctl start docker + +docker stop $CONTAINER_NAME +docker rm -f $CONTAINER_NAME +docker pull amneziavpn/openvpn:latest +docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/udp --name $CONTAINER_NAME amneziavpn/openvpn:latest -docker exec -i amneziavpn sh -c "mkdir -p /opt/amneziavpn_data/clients" +docker exec -i $CONTAINER_NAME sh -c "mkdir -p /opt/amneziavpn_data/clients" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa gen-dh" -#docker exec -i amneziavpn sh -c "cat /proc/sys/kernel/random/entropy_avail" -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && easyrsa gen-dh" - -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem /etc/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req MyReq nopass << EOF2 yes EOF2" -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && easyrsa sign-req server MyReq << EOF3 yes EOF3" -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && openvpn --genkey --secret ta.key << EOF4" -docker exec -i amneziavpn sh -c "cd /opt/amneziavpn_data && cp pki/ca.crt pki/issued/MyReq.crt pki/private/MyReq.key ta.key /etc/openvpn" -docker exec -i amneziavpn sh -c "openvpn --config /etc/openvpn/server.conf &" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem /etc/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req MyReq nopass << EOF2 yes EOF2" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa sign-req server MyReq << EOF3 yes EOF3" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && openvpn --genkey --secret ta.key << EOF4" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/ca.crt pki/issued/MyReq.crt pki/private/MyReq.key ta.key /etc/openvpn" +docker exec -d $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf" diff --git a/client/server_scripts/setup_shadowsocks_server.sh b/client/server_scripts/setup_shadowsocks_server.sh index 46df107a..6f147298 100644 --- a/client/server_scripts/setup_shadowsocks_server.sh +++ b/client/server_scripts/setup_shadowsocks_server.sh @@ -1,13 +1,24 @@ -#DOCKER_IMAGE="amneziavpn/shadow-vpn:latest" -#CONTAINER_NAME="shadow-vpn" +#CONTAINER_NAME=... this var will be set in ServerController -#sudo apt update -sudo apt install -y docker.io curl -sudo systemctl start docker +apt-get update -sudo docker stop shadow-vpn -sudo docker rm -f shadow-vpn -sudo docker pull amneziavpn/shadow-vpn:latest -sudo docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/tcp -p 6789:6789/tcp --name shadow-vpn amneziavpn/shadow-vpn:latest +iptables -P FORWARD ACCEPT + +apt install -y docker.io curl +systemctl start docker + +docker stop $CONTAINER_NAME +docker rm -f $CONTAINER_NAME +docker pull amneziavpn/shadowsocks:latest +docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/tcp -p 6789:6789/tcp --name $CONTAINER_NAME amneziavpn/shadowsocks:latest +docker exec -i $CONTAINER_NAME sh -c "mkdir -p /opt/amneziavpn_data/clients" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa gen-dh" + +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem /etc/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req MyReq nopass << EOF2 yes EOF2" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa sign-req server MyReq << EOF3 yes EOF3" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && openvpn --genkey --secret ta.key << EOF4" +docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/ca.crt pki/issued/MyReq.crt pki/private/MyReq.key ta.key /etc/openvpn" +docker exec -d $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf" diff --git a/client/server_scripts/template.ovpn b/client/server_scripts/template_openvpn.ovpn similarity index 99% rename from client/server_scripts/template.ovpn rename to client/server_scripts/template_openvpn.ovpn index da6d43c2..ee014358 100644 --- a/client/server_scripts/template.ovpn +++ b/client/server_scripts/template_openvpn.ovpn @@ -14,6 +14,7 @@ key-direction 1 remote-cert-tls server remote $REMOTE_HOST $REMOTE_PORT + $CA_CERT diff --git a/client/server_scripts/template_shadowsocks.ovpn b/client/server_scripts/template_shadowsocks.ovpn new file mode 100644 index 00000000..c68cac7d --- /dev/null +++ b/client/server_scripts/template_shadowsocks.ovpn @@ -0,0 +1,31 @@ +client +dev tun +proto $PROTO +resolv-retry infinite +nobind +persist-key +persist-tun +cipher AES-256-GCM +auth SHA512 +verb 3 +tls-client +tls-version-min 1.2 +key-direction 1 +remote-cert-tls server + +socks-proxy 127.0.0.1 $LOCAL_PROXY_PORT +route $REMOTE_HOST 255.255.255.255 net_gateway +remote $REMOTE_HOST $REMOTE_PORT + + +$CA_CERT + + +$CLIENT_CERT + + +$PRIV_KEY + + +$TA_KEY + diff --git a/client/settings.cpp b/client/settings.cpp index e0062c2b..5fdc23a0 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -1,32 +1,32 @@ -#include #include "defines.h" #include "settings.h" -Settings::Settings(QObject* parent) : QObject(parent) +Settings::Settings(QObject* parent) : + QObject(parent), + m_settings (ORGANIZATION_NAME, APPLICATION_NAME, this) { - m_settings = new QSettings(ORGANIZATION_NAME, APPLICATION_NAME, this); read(); } void Settings::read() { - m_settings->beginGroup("Server"); - m_userName = m_settings->value("userName", QString()).toString(); - m_password = m_settings->value("password", QString()).toString(); - m_serverName = m_settings->value("serverName", QString()).toString(); - m_serverPort = m_settings->value("serverPort", 22).toInt(); - m_settings->endGroup(); + m_settings.beginGroup("Server"); + m_userName = m_settings.value("userName", QString()).toString(); + m_password = m_settings.value("password", QString()).toString(); + m_serverName = m_settings.value("serverName", QString()).toString(); + m_serverPort = m_settings.value("serverPort", 22).toInt(); + m_settings.endGroup(); } void Settings::save() { - m_settings->beginGroup("Server"); - m_settings->setValue("userName", m_userName); - m_settings->setValue("password", m_password); - m_settings->setValue("serverName", m_serverName); - m_settings->setValue("serverPort", m_serverPort); - m_settings->endGroup(); + m_settings.beginGroup("Server"); + m_settings.setValue("userName", m_userName); + m_settings.setValue("password", m_password); + m_settings.setValue("serverName", m_serverName); + m_settings.setValue("serverPort", m_serverPort); + m_settings.endGroup(); } bool Settings::haveAuthData() const diff --git a/client/settings.h b/client/settings.h index 13177e30..1feb8d00 100644 --- a/client/settings.h +++ b/client/settings.h @@ -3,6 +3,7 @@ #include #include +#include #include "core/defs.h" @@ -35,8 +36,18 @@ public: bool haveAuthData() const; + + // list of sites to pass blocking added by user + QStringList customSites() { return m_settings.value("customSites").toStringList(); } + void setCustomSites(const QStringList &customSites) { m_settings.setValue("customSites", customSites); } + + // list of ips to pass blocking generated from customSites + QStringList customIps() { return m_settings.value("customIps").toStringList(); } + void setCustomIps(const QStringList &customIps) { m_settings.setValue("customIps", customIps); } + + protected: - QSettings* m_settings; + QSettings m_settings; QString m_userName; QString m_password; QString m_serverName; diff --git a/client/ui/macos_util.h b/client/ui/macos_util.h index 831f1f88..f5add902 100644 --- a/client/ui/macos_util.h +++ b/client/ui/macos_util.h @@ -3,6 +3,7 @@ #include #include +void setDockIconVisible(bool visible); void fixWidget(QWidget *widget); #endif diff --git a/client/ui/macos_util.mm b/client/ui/macos_util.mm index 9f433747..8adda58a 100644 --- a/client/ui/macos_util.mm +++ b/client/ui/macos_util.mm @@ -2,6 +2,19 @@ #include #include "macos_util.h" + +#import +#import + +void setDockIconVisible(bool visible) +{ + if (!visible) { + [NSApp setActivationPolicy: NSApplicationActivationPolicyAccessory]; + } else { + [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular]; + } +} + //this Objective-c class is used to override the action of system close button and zoom button //https://stackoverflow.com/questions/27643659/setting-c-function-as-selector-for-nsbutton-produces-no-results @interface ButtonPasser : NSObject{ diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index 595756f1..ad6a6be7 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -1,5 +1,8 @@ #include +#include #include +#include +#include #include #include #include @@ -17,7 +20,6 @@ #include "debug.h" #include "defines.h" #include "mainwindow.h" -#include "settings.h" #include "ui_mainwindow.h" #include "utils.h" #include "vpnconnection.h" @@ -27,23 +29,21 @@ #endif MainWindow::MainWindow(QWidget *parent) : -#ifdef Q_OS_WIN + #ifdef Q_OS_WIN CFramelessWindow(parent), -#else + #else QMainWindow(parent), -#endif + #endif ui(new Ui::MainWindow), - m_settings(new Settings), m_vpnConnection(nullptr) { ui->setupUi(this); + ui->label_error_text->clear(); ui->widget_tittlebar->installEventFilter(this); ui->stackedWidget_main->setSpeed(200); ui->stackedWidget_main->setAnimation(QEasingCurve::Linear); - ui->pushButton_blocked_list->setEnabled(false); - ui->pushButton_share_connection->setEnabled(false); #ifdef Q_OS_MAC ui->widget_tittlebar->hide(); ui->stackedWidget_main->move(0,0); @@ -52,7 +52,7 @@ MainWindow::MainWindow(QWidget *parent) : // Post initialization - if (m_settings->haveAuthData()) { + if (m_settings.haveAuthData()) { goToPage(Page::Vpn, true, false); } else { goToPage(Page::Start, true, false); @@ -61,6 +61,21 @@ MainWindow::MainWindow(QWidget *parent) : setupTray(); setupUiConnections(); + customSitesModel = new QStringListModel(); + ui->listView_sites_custom->setModel(customSitesModel); + + connect(ui->listView_sites_custom, &QListView::doubleClicked, [&](const QModelIndex &index){ + QDesktopServices::openUrl("https://" + index.data().toString()); + }); + connect(ui->lineEdit_sites_add_custom, &QLineEdit::returnPressed, [&](){ + ui->pushButton_sites_add_custom->click(); + }); + + initCustomSites(); + + ui->pushButton_general_settings_exit->hide(); + //ui->pushButton_share_connection->hide(); + setFixedSize(width(),height()); qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION); @@ -111,9 +126,9 @@ void MainWindow::goToPage(Page page, bool reset, bool slide) ui->label_server_settings_wait_info->hide(); ui->label_server_settings_wait_info->clear(); ui->label_server_settings_server->setText(QString("%1@%2:%3") - .arg(m_settings->userName()) - .arg(m_settings->serverName()) - .arg(m_settings->serverPort())); + .arg(m_settings.userName()) + .arg(m_settings.serverName()) + .arg(m_settings.serverPort())); } } @@ -188,6 +203,24 @@ void MainWindow::closeEvent(QCloseEvent *event) hide(); } +void MainWindow::showEvent(QShowEvent *event) +{ +#ifdef Q_OS_MACX + if (!event->spontaneous()) { + setDockIconVisible(true); + } +#endif +} + +void MainWindow::hideEvent(QHideEvent *event) +{ +#ifdef Q_OS_MACX + if (!event->spontaneous()) { + setDockIconVisible(false); + } +#endif +} + void MainWindow::onPushButtonNewServerConnectWithNewData(bool) { if (ui->lineEdit_new_server_ip->text().isEmpty() || @@ -209,14 +242,14 @@ void MainWindow::onPushButtonNewServerConnectWithNewData(bool) serverCredentials.password = ui->lineEdit_new_server_password->text(); bool ok = installServer(serverCredentials, - ui->page_new_server, - ui->progressBar_new_server_connection, - ui->pushButton_new_server_connect_with_new_data, - ui->label_new_server_wait_info); + ui->page_new_server, + ui->progressBar_new_server_connection, + ui->pushButton_new_server_connect_with_new_data, + ui->label_new_server_wait_info); if (ok) { - m_settings->setServerCredentials(serverCredentials); - m_settings->save(); + m_settings.setServerCredentials(serverCredentials); + m_settings.save(); goToPage(Page::Vpn); qApp->processEvents(); @@ -225,6 +258,29 @@ void MainWindow::onPushButtonNewServerConnectWithNewData(bool) } } +void MainWindow::onPushButtonNewServerConnectWithExistingCode(bool) +{ + QString s = ui->lineEdit_start_existing_code->text(); + 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(); + credentials.userName = o.value("u").toString(); + credentials.password = o.value("w").toString(); + + m_settings.setServerCredentials(credentials); + m_settings.save(); + + goToPage(Page::Vpn); + qDebug() << QString("Added server %3@%1:%2"). + arg(credentials.hostName). + arg(credentials.port). + arg(credentials.userName); +} + bool MainWindow::installServer(ServerCredentials credentials, QWidget *page, QProgressBar *progress, QPushButton *button, QLabel *info) { @@ -243,7 +299,7 @@ bool MainWindow::installServer(ServerCredentials credentials, timer.start(1000); - ErrorCode e = ServerController::setupServer(credentials, Protocol::Any); + ErrorCode e = ServerController::setupServer(credentials, Protocol::OpenVpn); if (e) { page->setEnabled(true); button->setVisible(true); @@ -251,8 +307,7 @@ bool MainWindow::installServer(ServerCredentials credentials, QMessageBox::warning(this, APPLICATION_NAME, tr("Error occurred while configuring server.") + "\n" + - errorString(e) + "\n" + - tr("See logs for details.")); + errorString(e)); return false; } @@ -287,7 +342,7 @@ bool MainWindow::installServer(ServerCredentials credentials, void MainWindow::onPushButtonReinstallServer(bool) { onDisconnect(); - installServer(m_settings->serverCredentials(), + installServer(m_settings.serverCredentials(), ui->page_server_settings, ui->progressBar_server_settings_reinstall, ui->pushButton_server_settings_reinstall, @@ -298,7 +353,7 @@ void MainWindow::onPushButtonClearServer(bool) { onDisconnect(); - ErrorCode e = ServerController::removeServer(m_settings->serverCredentials(), Protocol::Any); + ErrorCode e = ServerController::removeServer(m_settings.serverCredentials(), Protocol::Any); if (e) { QMessageBox::warning(this, APPLICATION_NAME, tr("Error occurred while configuring server.") + "\n" + @@ -317,12 +372,12 @@ void MainWindow::onPushButtonForgetServer(bool) { onDisconnect(); - m_settings->setUserName(""); - m_settings->setPassword(""); - m_settings->setServerName(""); - m_settings->setServerPort(); + m_settings.setUserName(""); + m_settings.setPassword(""); + m_settings.setServerName(""); + m_settings.setServerPort(); - m_settings->save(); + m_settings.save(); goToPage(Page::Start); } @@ -336,6 +391,8 @@ void MainWindow::onBytesChanged(quint64 receivedData, quint64 sentData) void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state) { + qDebug() << "MainWindow::onConnectionStateChanged" << VpnProtocol::textConnectionState(state); + bool pushButtonConnectEnabled = false; ui->label_state->setText(VpnProtocol::textConnectionState(state)); @@ -376,8 +433,7 @@ void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state) void MainWindow::onVpnProtocolError(ErrorCode errorCode) { - // TODO fix crash on Windows when starting vpn and another vpn already connected - //QMessageBox::critical(this, APPLICATION_NAME, errorString(errorCode)); + ui->label_error_text->setText(errorString(errorCode)); } void MainWindow::onPushButtonConnectClicked(bool checked) @@ -406,9 +462,11 @@ void MainWindow::setupTray() }); m_menu->addAction(QIcon(":/images/tray/cancel.png"), tr("Quit") + " " + APPLICATION_NAME, this, [&](){ +// QMessageBox::question(this, QMessageBox::question(this, tr("Exit"), tr("Do you really want to quit?"), QMessageBox::Yes | QMessageBox::No, ); + QMessageBox msgBox(QMessageBox::Question, tr("Exit"), tr("Do you really want to quit?"), - QMessageBox::Yes | QMessageBox::No, Q_NULLPTR, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint); - msgBox.setDefaultButton(QMessageBox::No); + QMessageBox::Yes | QMessageBox::No, this, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint); + msgBox.setDefaultButton(QMessageBox::Yes); msgBox.raise(); if (msgBox.exec() == QMessageBox::Yes) { qApp->quit(); @@ -455,6 +513,7 @@ void MainWindow::setupUiConnections() connect(ui->pushButton_connect, SIGNAL(clicked(bool)), this, SLOT(onPushButtonConnectClicked(bool))); connect(ui->pushButton_new_server_setup, &QPushButton::clicked, this, [this](){ goToPage(Page::NewServer); }); connect(ui->pushButton_new_server_connect_with_new_data, SIGNAL(clicked(bool)), this, SLOT(onPushButtonNewServerConnectWithNewData(bool))); + connect(ui->pushButton_new_server_connect, SIGNAL(clicked(bool)), this, SLOT(onPushButtonNewServerConnectWithExistingCode(bool))); connect(ui->pushButton_server_settings_reinstall, SIGNAL(clicked(bool)), this, SLOT(onPushButtonReinstallServer(bool))); connect(ui->pushButton_server_settings_clear, SIGNAL(clicked(bool)), this, SLOT(onPushButtonClearServer(bool))); @@ -463,7 +522,19 @@ void MainWindow::setupUiConnections() connect(ui->pushButton_blocked_list, &QPushButton::clicked, this, [this](){ goToPage(Page::Sites); }); connect(ui->pushButton_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); connect(ui->pushButton_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); - connect(ui->pushButton_share_connection, &QPushButton::clicked, this, [this](){ goToPage(Page::ShareConnection); }); + connect(ui->pushButton_share_connection, &QPushButton::clicked, this, [this](){ + goToPage(Page::ShareConnection); + updateShareCode(); + }); + + connect(ui->pushButton_copy_sharing_code, &QPushButton::clicked, this, [this](){ + QGuiApplication::clipboard()->setText(ui->textEdit_sharing_code->toPlainText()); + ui->pushButton_copy_sharing_code->setText(tr("Copied")); + + QTimer::singleShot(3000, [this]() { + ui->pushButton_copy_sharing_code->setText(tr("Copy")); + }); + }); connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); }); @@ -472,6 +543,8 @@ void MainWindow::setupUiConnections() 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(); }); } void MainWindow::setTrayState(VpnProtocol::ConnectionState state) @@ -508,32 +581,35 @@ void MainWindow::setTrayState(VpnProtocol::ConnectionState state) setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName)); } -//#ifdef Q_OS_MAC -// // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user ) -// bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme(); -// darkTaskBar = forceUseBrightIcons ? true : darkTaskBar; -// resourcesPath = ":/images_mac/tray_icon/%1"; -// useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png"); -//#endif + //#ifdef Q_OS_MAC + // // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user ) + // bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme(); + // darkTaskBar = forceUseBrightIcons ? true : darkTaskBar; + // resourcesPath = ":/images_mac/tray_icon/%1"; + // useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png"); + //#endif } void MainWindow::onTrayActivated(QSystemTrayIcon::ActivationReason reason) { +#ifndef Q_OS_MAC if(reason == QSystemTrayIcon::DoubleClick || reason == QSystemTrayIcon::Trigger) { show(); raise(); setWindowState(Qt::WindowActive); } +#endif } void MainWindow::onConnect() { + ui->label_error_text->clear(); ui->pushButton_connect->setChecked(true); qApp->processEvents(); // TODO: Call connectToVpn with restricted server account - ServerCredentials credentials = m_settings->serverCredentials(); + ServerCredentials credentials = m_settings.serverCredentials(); ErrorCode errorCode = m_vpnConnection->connectToVpn(credentials); if (errorCode) { @@ -541,6 +617,7 @@ void MainWindow::onConnect() QMessageBox::critical(this, APPLICATION_NAME, errorString(errorCode)); return; } + ui->pushButton_connect->setEnabled(false); } @@ -559,3 +636,89 @@ void MainWindow::onTrayActionConnect() } } + +void MainWindow::onPushButtonAddCustomSitesClicked() +{ + QString newSite = ui->lineEdit_sites_add_custom->text(); + + if (newSite.isEmpty()) return; + if (!newSite.contains(".")) return; + + // get domain name if it present + newSite.replace("https://", ""); + newSite.replace("http://", ""); + newSite.replace("ftp://", ""); + + newSite = newSite.split("/", QString::SkipEmptyParts).first(); + + + QStringList customSites = m_settings.customSites(); + if (!customSites.contains(newSite)) { + customSites.append(newSite); + m_settings.setCustomSites(customSites); + + QString newIp = Utils::getIPAddress(newSite); + QStringList customIps = m_settings.customIps(); + if (!newIp.isEmpty() && !customIps.contains(newIp)) { + customIps.append(newIp); + m_settings.setCustomIps(customIps); + + // add to routes immediatelly +// if (vpnStatus() == VPNStatusConnected) { +// //Router::Instance().routeAdd(newIp, vpnGate()); +// } + } + + initCustomSites(); + + ui->lineEdit_sites_add_custom->clear(); + } + else { + qDebug() << "customSites already contains" << newSite; + } +} + +void MainWindow::onPushButtonDeleteCustomSiteClicked() +{ + QModelIndex index = ui->listView_sites_custom->currentIndex(); + QString siteToDelete = index.data(Qt::DisplayRole).toString(); + + if (siteToDelete.isEmpty()) { + return; + } + + QString ipToDelete = Utils::getIPAddress(siteToDelete); + + QStringList customSites = m_settings.customSites(); + customSites.removeAll(siteToDelete); + qDebug() << "Deleted custom site:" << siteToDelete; + m_settings.setCustomSites(customSites); + + QStringList customIps = m_settings.customIps(); + customIps.removeAll(ipToDelete); + qDebug() << "Deleted custom ip:" << ipToDelete; + m_settings.setCustomIps(customIps); + + + initCustomSites(); + + //Router::Instance().routeDelete(Utils::getIPAddress(ipToDelete)); + //Router::Instance().flushDns(); +} + +void MainWindow::initCustomSites() +{ + customSitesModel->setStringList(m_settings.customSites()); +} + +void MainWindow::updateShareCode() +{ + QJsonObject o; + o.insert("h", m_settings.serverName()); + o.insert("p", m_settings.serverPort()); + o.insert("u", m_settings.userName()); + o.insert("w", m_settings.password()); + + QByteArray ba = QJsonDocument(o).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + ui->textEdit_sharing_code->setText(QString("vpn://%1").arg(QString(ba))); +} diff --git a/client/ui/mainwindow.h b/client/ui/mainwindow.h index db3cae1a..90a4e5e0 100644 --- a/client/ui/mainwindow.h +++ b/client/ui/mainwindow.h @@ -5,12 +5,14 @@ #include #include #include +#include #include #include "framelesswindow.h" #include "protocols/vpnprotocol.h" -class Settings; +#include "settings.h" + class VpnConnection; namespace Ui { @@ -43,11 +45,15 @@ private slots: void onPushButtonConnectClicked(bool checked); void onPushButtonNewServerConnectWithNewData(bool); + void onPushButtonNewServerConnectWithExistingCode(bool); void onPushButtonReinstallServer(bool); void onPushButtonClearServer(bool); void onPushButtonForgetServer(bool); + void onPushButtonAddCustomSitesClicked(); + void onPushButtonDeleteCustomSiteClicked(); + void onTrayActionConnect(); // connect from context menu void setTrayState(VpnProtocol::ConnectionState state); @@ -68,10 +74,14 @@ private: void setTrayIcon(const QString &iconPath); void setupUiConnections(); + void initCustomSites(); + + void updateShareCode(); + Ui::MainWindow *ui; VpnConnection* m_vpnConnection; - Settings* m_settings; + Settings m_settings; QAction* m_trayActionConnect; QAction* m_trayActionDisconnect; @@ -79,11 +89,15 @@ private: QSystemTrayIcon m_tray; QMenu* m_menu; + QStringListModel *customSitesModel = nullptr; + bool canMove = false; QPoint offset; bool eventFilter(QObject *obj, QEvent *event) override; void keyPressEvent(QKeyEvent* event) override; void closeEvent(QCloseEvent *event) override; + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; const QString ConnectedTrayIconName = "active.png"; const QString DisconnectedTrayIconName = "default.png"; diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index 6c20db41..10ff3837 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -14,26 +14,7 @@ - QWidget { - font: 16px "Lato"; - outline: none; - font-style: normal; - font-weight: normal; -} - -/*----------------------*/ - -QPushButton { - border: none; - color: rgb(216, 216, 216); -} -QPushButton:disabled { - border: none; - color: rgb(127, 127, 127); -} - -/*----------------------*/ - + QLabel { color: #181922; } @@ -41,14 +22,14 @@ QLabel:disabled { color: #A7A7A7; } -/*----------------------*/ - -QMessageBox { - background-color: #333333; -} QMessageBox QLabel { - color: #aaa; + font: 16px "Lato"; } +/* +QMessageBox QPushButton { + border: 1; +} +*/ /*----------------------*/ @@ -176,7 +157,25 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - QWidget #widget_main { + QWidget { + font: 16px "Lato"; + outline: none; + font-style: normal; + font-weight: normal; +} + +/*----------------------*/ + +QPushButton { + border: none; +} +QPushButton:disabled { + border: none; +} + +/*----------------------*/ + +QWidget #widget_main { background: white; } @@ -257,17 +256,10 @@ QPushButton:hover { - QStackedWidget#stackedWidget_main{ - background: transparent; -} - -QStackedWidget QWidget { - background: transparent; -} - + - 1 + 0 @@ -365,7 +357,7 @@ border-radius: 4px; - + 40 @@ -384,6 +376,9 @@ color: #333333; + + vpn://... + @@ -902,15 +897,15 @@ QPushButton:hover { Lato - 12 - 7 + -1 + 50 false false color: rgb(66, 209, 133); -font: 63 12pt "Lato"; +font: 16px "Lato"; 0 Mbps @@ -931,15 +926,15 @@ font: 63 12pt "Lato"; Lato - 12 - 7 + -1 + 50 false false color: rgb(65, 113, 214); -font: 63 12pt "Lato"; +font: 16px "Lato"; 0 Mbps @@ -1102,7 +1097,7 @@ color: #181922; Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - + false @@ -1121,7 +1116,7 @@ color: #181922; true - + false @@ -1140,6 +1135,45 @@ color: #181922; false + + + + 0 + 300 + 381 + 51 + + + + Error text + + + false + + + Qt::AlignCenter + + + true + + + + + + 10 + 460 + 361 + 141 + + + + image: url(:/images/AmneziaVPN.png); +background-color: rgb(255, 255, 255); + + + + + @@ -1265,7 +1299,7 @@ line-height: 25px; color: #181922; - These sites will open via VPN + These sites will be opened using VPN Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -1322,7 +1356,7 @@ border: 1px solid #A7A7A7; Qt::AlignCenter - For example, rutor.org or 17.21.111.8 + For example, yousite.com or 17.21.111.8 @@ -1341,10 +1375,10 @@ border: 1px solid #A7A7A7; PointingHandCursor - /* black */ -background: #181922; + background: #100A44; border-radius: 4px; -font-size: 18pt; +font-size: 24px; +color: white + @@ -1369,7 +1403,7 @@ font-size: 18pt; color: #181922; - Delete selected item + Delete selected site @@ -1394,7 +1428,7 @@ line-height: 150%; color: #333333; - Hostname or IP address + Web site or hostname or IP address @@ -1909,6 +1943,9 @@ color: #333333; label_server_settings_server + + + @@ -1975,17 +2012,19 @@ color: #181922; true - + 30 100 320 - 151 + 211 - background: #F5F5F5; + QTextEdit { + +background: #F5F5F5; border-radius: 10px; @@ -1998,27 +2037,32 @@ line-height: 110%; text-align: center; color: #15CDCB; + +} QTextEdit::FixedColumnWidth - 20 + 30 + + + true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Consolas'; font-size:22px; font-weight:600; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt;">vpn:\\aosdiufhafafsuhgqejghuserhglaidhgauhgalgadg</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt;">vpn:\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</span></p></body></html> - + 20 - 280 + 340 341 40 @@ -2052,7 +2096,7 @@ line-height: 21px; 30 - 350 + 400 321 101 diff --git a/client/utils.cpp b/client/utils.cpp index 5f23bb49..ad20df2b 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -111,3 +111,71 @@ bool Utils::processIsRunning(const QString& fileName) #endif } +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(checkIPFormat(host)) { + qDebug().noquote() << "GetIPAddress host is ip:" << host << host; + return host; // it is a ip address. + } + 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; +} + +QString Utils::getStringBetween(const QString& s, const QString& a, const QString& b) +{ + int ap = s.indexOf(a), bp = s.indexOf(b, ap + a.length()); + if(ap < 0 || bp < 0) + return QString(); + ap += a.length(); + if(bp - ap <= 0) + return QString(); + return s.mid(ap, bp - ap).trimmed(); +} + +bool Utils::checkIPFormat(const QString& ip) +{ + int count = ip.count("."); + if(count != 3) + return false; + + QStringList list = ip.trimmed().split("."); + foreach(QString it, list) { + if(it.toInt() <= 255 && it.toInt() >= 0) + continue; + return false; + } + return true; +} diff --git a/client/utils.h b/client/utils.h index 878cf01b..23eed83b 100644 --- a/client/utils.h +++ b/client/utils.h @@ -15,6 +15,10 @@ public: static bool createEmptyFile(const QString& path); static bool initializePath(const QString& path); static bool processIsRunning(const QString& fileName); + + static QString getIPAddress(const QString& host); + static QString getStringBetween(const QString& s, const QString& a, const QString& b); + static bool checkIPFormat(const QString& ip); }; #endif // UTILS_H diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index ce2cb0fa..6e9c0571 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -6,8 +6,10 @@ #include #include "protocols/openvpnprotocol.h" +#include "protocols/shadowsocksvpnprotocol.h" #include "utils.h" #include "vpnconnection.h" +#include "communicator.h" VpnConnection::VpnConnection(QObject* parent) : QObject(parent) { @@ -21,6 +23,25 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) { +// if (state == VpnProtocol::ConnectionState::Connected){ +// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList())); + +// // add routes +// const QStringList &black_custom = m_settings.customIps(); +// qDebug() << "onConnect :: adding custom black routes, count:" << black_custom.size(); + + +// QStringList args; +// args << m_vpnProtocol->vpnGateway(); +// args << black_custom; + +// Message m(Message::State::RoutesAddRequest, args); +// m_vpnProtocol->communicator()->sendMessage(m); +// } +// else if (state == VpnProtocol::ConnectionState::Error) { +// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::ClearSavedRoutesRequest, QStringList())); +// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList())); +// } emit connectionStateChanged(state); } @@ -36,8 +57,8 @@ ErrorCode VpnConnection::lastError() const ErrorCode VpnConnection::requestVpnConfig(const ServerCredentials &credentials, Protocol protocol) { ErrorCode errorCode = ErrorCode::NoError; - if (protocol == Protocol::OpenVpn) { - QString configData = OpenVpnConfigurator::genOpenVpnConfig(credentials, &errorCode); + if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocks) { + QString configData = OpenVpnConfigurator::genOpenVpnConfig(credentials, protocol, &errorCode); if (errorCode) { return errorCode; } @@ -51,8 +72,7 @@ ErrorCode VpnConnection::requestVpnConfig(const ServerCredentials &credentials, return ErrorCode::FailedToSaveConfigData; } - else if (protocol == Protocol::ShadowSocks) { - // Request OpenVPN config and ShadowSocks + else { return ErrorCode::NotImplementedError; } return ErrorCode::NotImplementedError; @@ -61,6 +81,8 @@ ErrorCode VpnConnection::requestVpnConfig(const ServerCredentials &credentials, ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Protocol protocol) { + // protocol = Protocol::ShadowSocks; + // TODO: Try protocols one by one in case of Protocol::Any // TODO: Implement some behavior in case if connection not stable qDebug() << "Connect to VPN"; @@ -81,8 +103,18 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); } else if (protocol == Protocol::ShadowSocks) { - emit connectionStateChanged(VpnProtocol::ConnectionState::Error); - return ErrorCode::NotImplementedError; + ErrorCode e = requestVpnConfig(credentials, Protocol::ShadowSocks); + if (e) { + emit connectionStateChanged(VpnProtocol::ConnectionState::Error); + return e; + } + if (m_vpnProtocol) { + disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); + } + + m_vpnProtocol.reset(new ShadowSocksVpnProtocol(ShadowSocksVpnProtocol::genShadowSocksConfig(credentials))); + connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); + } connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); @@ -101,12 +133,21 @@ 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())); + if (!m_vpnProtocol.data()) { return; } m_vpnProtocol.data()->stop(); } +VpnProtocol::ConnectionState VpnConnection::connectionState() +{ + if (!m_vpnProtocol) return VpnProtocol::ConnectionState::Disconnected; + return m_vpnProtocol->connectionState(); +} + bool VpnConnection::onConnected() const { if (!m_vpnProtocol.data()) { diff --git a/client/vpnconnection.h b/client/vpnconnection.h index d4f13d65..70f4b185 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -7,6 +7,7 @@ #include "protocols/vpnprotocol.h" #include "core/defs.h" +#include "settings.h" using namespace amnezia; @@ -27,6 +28,8 @@ public: bool onDisconnected() const; void disconnectFromVpn(); + VpnProtocol::ConnectionState connectionState(); + signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); @@ -39,6 +42,9 @@ protected slots: protected: QScopedPointer m_vpnProtocol; + +private: + Settings m_settings; }; #endif // VPNCONNECTION_H diff --git a/deploy/PrivacyTechAppleCert.p12 b/deploy/PrivacyTechAppleCert.p12 new file mode 100644 index 00000000..f8b91957 Binary files /dev/null and b/deploy/PrivacyTechAppleCert.p12 differ diff --git a/deploy/PrivacyTechWindowsCert.pfx b/deploy/PrivacyTechWindowsCert.pfx new file mode 100644 index 00000000..60e139fc Binary files /dev/null and b/deploy/PrivacyTechWindowsCert.pfx differ diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh new file mode 100644 index 00000000..664b811c --- /dev/null +++ b/deploy/build_macos.sh @@ -0,0 +1,94 @@ +#!/bin/bash +echo "Build script started ..." + +set -o errexit -o nounset + +# Hold on to current directory +PROJECT_DIR=$(pwd) +SCRIPT_DIR=$PROJECT_DIR/deploy + +mkdir -p $SCRIPT_DIR/build +WORK_DIR=$SCRIPT_DIR/build + +echo "Project dir: ${PROJECT_DIR}" +echo "Build dir: ${WORK_DIR}" + +APP_NAME=AmneziaVPN +APP_FILENAME=$APP_NAME.app +APP_DOMAIN=org.amneziavpn.package +PLIST_NAME=$APP_NAME.plist + +RELEASE_DIR=$WORK_DIR +OUT_APP_DIR=$RELEASE_DIR/client +BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME +DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos +INSTALLER_DATA_DIR=$RELEASE_DIR/installer/packages/$APP_DOMAIN/data + +PRO_FILE_PATH=$PROJECT_DIR/$APP_NAME.pro +QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash +TARGET_FILENAME=$PROJECT_DIR/$APP_NAME.dmg + +# Seacrh Qt +echo "Brew Qt version $(brew --prefix qt)" + + +#if [ -f $(brew --prefix qt)/clang_64/bin/qmake ]; then QT_BIN_DIR=$(brew --prefix qt)/clang_64/bin; +#else QT_BIN_DIR=$HOME/Qt/5.14.2/clang_64/bin; fi + +QT_BIN_DIR=$HOME/Qt/5.14.2/clang_64/bin + +#QIF_BIN_DIR=$HOME/Qt/Tools/QtInstallerFramework/4.0/bin +QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/4.0/bin + +echo "Using Qt in $QT_BIN_DIR" +echo "Using QIF in $QIF_BIN_DIR" + +ls -al $QT_BIN_DIR/../../.. + + +# Checking env +$QT_BIN_DIR/qmake -v +make -v +clang -v + +# Build App +echo "Building App..." +cd $WORK_DIR + +$QT_BIN_DIR/qmake $PROJECT_DIR/AmneziaVPN.pro 'CONFIG+=release CONFIG+=x86_64' +make -j `sysctl -n hw.ncpu` + +# Build and run tests here + +echo "____________________________________" +echo "............Deploy.................." +echo "____________________________________" + +# Package +echo "Packaging ..." + +#cd $SCRIPT_DIR + +$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite +cp -av $RELEASE_DIR/service/server/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS +cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS + + +mkdir -p $INSTALLER_DATA_DIR +cp -av $PROJECT_DIR/deploy/installer $RELEASE_DIR +cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install.sh +cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh +cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME + +rm -f $BUNDLE_DIR/Contents/macOS/post_install.sh $BUNDLE_DIR/Contents/macOS/post_uninstall.sh +chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall.sh + +cd $BUNDLE_DIR +tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./ + +cd $RELEASE_DIR/installer +$QIF_BIN_DIR/binarycreator --offline-only -v -c config/macos.xml -p packages -f $APP_NAME +hdiutil create -volname $APP_NAME -srcfolder $APP_NAME.app -ov -format UDZO $TARGET_FILENAME + + +echo "Finished, artifact is $PROJECT_DIR/$APP_NAME.dmg" diff --git a/deploy/build_windows.bat b/deploy/build_windows.bat new file mode 100644 index 00000000..b428ee48 --- /dev/null +++ b/deploy/build_windows.bat @@ -0,0 +1,101 @@ + @ECHO OFF + +CHCP 1252 + +REM %VAR:"=% mean dequoted %VAR% + +set QT_BIN_DIR="c:\Qt\5.14.2\msvc2017\bin" +set QIF_BIN_DIR="c:\Qt\Tools\QtInstallerFramework\4.0\bin" + +set PATH=%QT_BIN_DIR:"=%;%PATH% + +echo "Using Qt in %QT_BIN_DIR%" +echo "Using QIF in %QIF_BIN_DIR%" + +REM Hold on to current directory +set PROJECT_DIR=%cd% +set SCRIPT_DIR=%PROJECT_DIR:"=%\deploy + +rmdir /Q /S %WORK_DIR% +mkdir %SCRIPT_DIR:"=%\build +set WORK_DIR=%SCRIPT_DIR:"=%\build + + +set APP_NAME=AmneziaVPN +set APP_FILENAME=%APP_NAME:"=%.exe +set APP_DOMAIN=org.amneziavpn.package +set RELEASE_DIR=%WORK_DIR:"=% +set OUT_APP_DIR=%RELEASE_DIR:"=%\client\release +set DEPLOY_DATA_DIR=%SCRIPT_DIR:"=%\data\windows +set INSTALLER_DATA_DIR=%RELEASE_DIR:"=%\installer\packages\%APP_DOMAIN:"=%\data +set PRO_FILE_PATH=%PROJECT_DIR:"=%\%APP_NAME:"=%.pro +set QMAKE_STASH_FILE=%PROJECT_DIR:"=%\.qmake_stash +set TARGET_FILENAME=%PROJECT_DIR:"=%\%APP_NAME:"=%.exe + +echo "Environment:" +echo "APP_FILENAME: %APP_FILENAME%" +echo "PROJECT_DIR: %PROJECT_DIR%" +echo "SCRIPT_DIR: %SCRIPT_DIR%" +echo "RELEASE_DIR: %RELEASE_DIR%" +echo "OUT_APP_DIR: %OUT_APP_DIR%" +echo "DEPLOY_DATA_DIR: %DEPLOY_DATA_DIR%" +echo "INSTALLER_DATA_DIR: %INSTALLER_DATA_DIR%" +echo "PRO_FILE_PATH: %PRO_FILE_PATH%" +echo "QMAKE_STASH_FILE: %QMAKE_STASH_FILE%" +echo "TARGET_FILENAME: %TARGET_FILENAME%" + +echo "Cleanup..." +Rmdir /Q /S %RELEASE_DIR% +Del %QMAKE_STASH_FILE% +Del %TARGET_FILENAME% + +"%QT_BIN_DIR:"=%\qmake" -v +"%QT_BIN_DIR:"=%\windeployqt" -v +nmake /? + +cd %PROJECT_DIR% +"%QT_BIN_DIR:"=%\qmake" -spec win32-msvc -o deploy\build\Makefile + +cd %WORK_DIR% +set CL=/MP +nmake /A /NOLOGO +break +nmake clean +rem if not exist "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" break + +echo "Deploying..." + +copy "%WORK_DIR:"=%\service\server\release\%APP_NAME:"=%-service.exe" %OUT_APP_DIR% +copy "%WORK_DIR:"=%\platform\post-uninstall\release\post-uninstall.exe" %OUT_APP_DIR% + +echo "Signing exe" +cd %OUT_APP_DIR% +signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.exe + +"%QT_BIN_DIR:"=%\windeployqt" --release --force --no-translations "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" +signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.dll + + +echo "Copying deploy data..." +xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f + + +cd %SCRIPT_DIR% +xcopy %SCRIPT_DIR:"=%\installer %RELEASE_DIR:"=%\installer /s /e /y /i /f +mkdir %INSTALLER_DATA_DIR% + +echo "Deploy finished, content:" +dir %OUT_APP_DIR% + +cd %OUT_APP_DIR% +echo "Compressing data..." +"%QIF_BIN_DIR:"=%\archivegen" -c 9 %INSTALLER_DATA_DIR:"=%\%APP_NAME:"=%.7z ./ + +cd "%RELEASE_DIR:"=%\installer" +echo "Creating installer..." +"%QIF_BIN_DIR:"=%\binarycreator" --offline-only -v -c config\windows.xml -p packages -f %TARGET_FILENAME% + +cd %PROJECT_DIR% +signtool sign /f "%SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx" /p %WIN_CERT_PW% /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 %TARGET_FILENAME% + +echo "Finished, see %TARGET_FILENAME%" diff --git a/deploy/data/macos/openvpn b/deploy/data/macos/openvpn old mode 100644 new mode 100755 diff --git a/deploy/data/macos/post_install.sh b/deploy/data/macos/post_install.sh old mode 100644 new mode 100755 diff --git a/deploy/data/macos/post_uninstall.sh b/deploy/data/macos/post_uninstall.sh old mode 100644 new mode 100755 diff --git a/deploy/data/windows/ss/LICENSE b/deploy/data/windows/ss/LICENSE new file mode 100644 index 00000000..e963df82 --- /dev/null +++ b/deploy/data/windows/ss/LICENSE @@ -0,0 +1,622 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + diff --git a/deploy/data/windows/ss/libbloom.dll b/deploy/data/windows/ss/libbloom.dll new file mode 100644 index 00000000..d60114d1 Binary files /dev/null and b/deploy/data/windows/ss/libbloom.dll differ diff --git a/deploy/data/windows/ss/libcares-4.dll b/deploy/data/windows/ss/libcares-4.dll new file mode 100644 index 00000000..784827f6 Binary files /dev/null and b/deploy/data/windows/ss/libcares-4.dll differ diff --git a/deploy/data/windows/ss/libcork.dll b/deploy/data/windows/ss/libcork.dll new file mode 100644 index 00000000..49e841ee Binary files /dev/null and b/deploy/data/windows/ss/libcork.dll differ diff --git a/deploy/data/windows/ss/libev-4.dll b/deploy/data/windows/ss/libev-4.dll new file mode 100644 index 00000000..78d2378f Binary files /dev/null and b/deploy/data/windows/ss/libev-4.dll differ diff --git a/deploy/data/windows/ss/libgcc_s_dw2-1.dll b/deploy/data/windows/ss/libgcc_s_dw2-1.dll new file mode 100644 index 00000000..366eaced Binary files /dev/null and b/deploy/data/windows/ss/libgcc_s_dw2-1.dll differ diff --git a/deploy/data/windows/ss/libipset.dll b/deploy/data/windows/ss/libipset.dll new file mode 100644 index 00000000..ad639557 Binary files /dev/null and b/deploy/data/windows/ss/libipset.dll differ diff --git a/deploy/data/windows/ss/libmbedcrypto.dll b/deploy/data/windows/ss/libmbedcrypto.dll new file mode 100644 index 00000000..4deb59b8 Binary files /dev/null and b/deploy/data/windows/ss/libmbedcrypto.dll differ diff --git a/deploy/data/windows/ss/libpcre-1.dll b/deploy/data/windows/ss/libpcre-1.dll new file mode 100644 index 00000000..77d92128 Binary files /dev/null and b/deploy/data/windows/ss/libpcre-1.dll differ diff --git a/deploy/data/windows/ss/libshadowsocks-libev.a b/deploy/data/windows/ss/libshadowsocks-libev.a new file mode 100644 index 00000000..52b2b200 Binary files /dev/null and b/deploy/data/windows/ss/libshadowsocks-libev.a differ diff --git a/deploy/data/windows/ss/libshadowsocks-libev.dll b/deploy/data/windows/ss/libshadowsocks-libev.dll new file mode 100644 index 00000000..b95b2eab Binary files /dev/null and b/deploy/data/windows/ss/libshadowsocks-libev.dll differ diff --git a/deploy/data/windows/ss/libshadowsocks-libev.dll.a b/deploy/data/windows/ss/libshadowsocks-libev.dll.a new file mode 100644 index 00000000..985455ae Binary files /dev/null and b/deploy/data/windows/ss/libshadowsocks-libev.dll.a differ diff --git a/deploy/data/windows/ss/libsodium-23.dll b/deploy/data/windows/ss/libsodium-23.dll new file mode 100644 index 00000000..0162a0ec Binary files /dev/null and b/deploy/data/windows/ss/libsodium-23.dll differ diff --git a/deploy/data/windows/ss/libwinpthread-1.dll b/deploy/data/windows/ss/libwinpthread-1.dll new file mode 100644 index 00000000..7ca9075f Binary files /dev/null and b/deploy/data/windows/ss/libwinpthread-1.dll differ diff --git a/deploy/data/windows/ss/shadowsocks.h b/deploy/data/windows/ss/shadowsocks.h new file mode 100644 index 00000000..6c36611f --- /dev/null +++ b/deploy/data/windows/ss/shadowsocks.h @@ -0,0 +1,101 @@ +/* + * shadowsocks.h - Header files of library interfaces + * + * Copyright (C) 2013 - 2019, Max Lv + * + * 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 + * . + */ + +#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 diff --git a/deploy/data/windows/ss/ss-local.exe b/deploy/data/windows/ss/ss-local.exe new file mode 100644 index 00000000..cb321288 Binary files /dev/null and b/deploy/data/windows/ss/ss-local.exe differ diff --git a/deploy/data/windows/ss/ss-server.exe b/deploy/data/windows/ss/ss-server.exe new file mode 100644 index 00000000..5df39a0c Binary files /dev/null and b/deploy/data/windows/ss/ss-server.exe differ diff --git a/deploy/data/windows/ss/ss-tunnel.exe b/deploy/data/windows/ss/ss-tunnel.exe new file mode 100644 index 00000000..791a8e1c Binary files /dev/null and b/deploy/data/windows/ss/ss-tunnel.exe differ diff --git a/deploy/install-qt.sh b/deploy/install-qt.sh new file mode 100644 index 00000000..f7c3d256 --- /dev/null +++ b/deploy/install-qt.sh @@ -0,0 +1,335 @@ +#!/usr/bin/env bash +############################################################################# +## +## Copyright (C) 2019 Richard Weickelt. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qbs. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +set -eu + +function help() { + cat < + Root directory where to install the components. + Maps to C:/Qt on Windows, /opt/Qt on Linux, /usr/local/Qt on Mac + by default. + + -f, --force + Force download and do not attempt to re-use an existing installation. + + --host + The host operating system. Can be one of linux_x64, mac_x64, + windows_x86. Auto-detected by default. + + --target + The desired target platform. Can be one of desktop, android, ios. + The default value is desktop. + + --toolchain + The toolchain that has been used to build the binaries. + Possible values depend on --host and --target, respectively: + + linux_x64 + android + any, android_armv7, android_arm64_v8a + desktop + gcc_64 (default) + + mac_x64 + android + any, android_armv7, android_arm64_v8a + desktop + clang_64 (default), + ios + ios + + windows_x86 + android + any, android_armv7, android_arm64_v8a + desktop + win64_mingw73, win64_msvc2017_64 (default) + + --version + The desired Qt version. Currently supported are all versions + above 5.9.0. + +EOF +} + +TARGET_PLATFORM=desktop +COMPONENTS= +VERSION= +FORCE_DOWNLOAD=false +MD5_TOOL=md5sum + +case "$OSTYPE" in + *linux*) + HOST_OS=linux_x64 + INSTALL_DIR=/opt/Qt + TOOLCHAIN=gcc_64 + ;; + *darwin*) + HOST_OS=mac_x64 + INSTALL_DIR=/usr/local/Qt + TOOLCHAIN=clang_64 + MD5_TOOL="md5 -r" + ;; + msys) + HOST_OS=windows_x86 + INSTALL_DIR=/c/Qt + TOOLCHAIN=win64_msvc2015_64 + ;; + *) + HOST_OS= + INSTALL_DIR= + ;; +esac + +while [ $# -gt 0 ]; do + case "$1" in + --directory|-d) + INSTALL_DIR="$2" + shift + ;; + --force|-f) + FORCE_DOWNLOAD=true + ;; + --host) + HOST_OS="$2" + shift + ;; + --target) + TARGET_PLATFORM="$2" + shift + ;; + --toolchain) + TOOLCHAIN=$(echo $2 | tr '[A-Z]' '[a-z]') + shift + ;; + --version) + VERSION="$2" + shift + ;; + --help|-h) + help + exit 0 + ;; + *) + COMPONENTS="${COMPONENTS} $1" + ;; + esac + shift +done + +if [ -z "${HOST_OS}" ]; then + echo "No --host specified or auto-detection failed." >&2 + exit 1 +fi + +if [ -z "${INSTALL_DIR}" ]; then + echo "No --directory specified or auto-detection failed." >&2 + exit 1 +fi + +if [ -z "${VERSION}" ]; then + echo "No --version specified." >&2 + exit 1 +fi + +if [ -z "${COMPONENTS}" ]; then + echo "No components specified." >&2 + exit 1 +fi + +case "$TARGET_PLATFORM" in + android) + ;; + ios) + ;; + desktop) + ;; + *) + echo "Error: TARGET_PLATFORM=${TARGET_PLATFORM} is not valid." >&2 + exit 1 + ;; +esac + +HASH=$(echo "${OSTYPE} ${TARGET_PLATFORM} ${TOOLCHAIN} ${VERSION} ${INSTALL_DIR}" | ${MD5_TOOL} | head -c 16) +HASH_FILEPATH="${INSTALL_DIR}/${HASH}.manifest" +INSTALLATION_IS_VALID=false +if ! ${FORCE_DOWNLOAD} && [ -f "${HASH_FILEPATH}" ]; then + INSTALLATION_IS_VALID=true + while read filepath; do + if [ ! -e "${filepath}" ]; then + INSTALLATION_IS_VALID=false + break + fi + done <"${HASH_FILEPATH}" +fi + +if ${INSTALLATION_IS_VALID}; then + echo "Already installed. Skipping download." >&2 + exit 0 +fi + +DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'install-qt'` + +# +# The repository structure is a mess. Try different URL variants +# +function compute_url(){ + local COMPONENT=$1 + local CURL="curl -s -L" + local BASE_URL="http://download.qt.io/online/qtsdkrepository/${HOST_OS}/${TARGET_PLATFORM}" + local ANDROID_ARCH=$(echo ${TOOLCHAIN##android_}) + + if [[ "${COMPONENT}" =~ "qtcreator" ]]; then + + SHORT_VERSION=${VERSION%??} + BASE_URL="http://download.qt.io/official_releases/qtcreator" + REMOTE_PATH="${SHORT_VERSION}/${VERSION}/installer_source/${HOST_OS}/qtcreator.7z" + echo "${BASE_URL}/${REMOTE_PATH}" + return 0 + + else + REMOTE_BASES=( + # New repository format (>=6.0.0) + "qt6_${VERSION//./}/qt.qt6.${VERSION//./}.${TOOLCHAIN}" + "qt6_${VERSION//./}_${ANDROID_ARCH}/qt.qt6.${VERSION//./}.${TOOLCHAIN}" + "qt6_${VERSION//./}_${ANDROID_ARCH}/qt.qt6.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}" + # New repository format (>=5.9.6) + "qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${TOOLCHAIN}" + "qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}" + # Multi-abi Android since 5.14 + "qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${TARGET_PLATFORM}" + "qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${COMPONENT}.${TARGET_PLATFORM}" + # Older repository format (<5.9.0) + "qt5_${VERSION//./}/qt.${VERSION//./}.${TOOLCHAIN}" + "qt5_${VERSION//./}/qt.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}" + ) + + for REMOTE_BASE in ${REMOTE_BASES[*]}; do + REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "[[:alnum:]_.\-]*7z" | grep "${COMPONENT}" | tail -1)" + if [ ! -z "${REMOTE_PATH}" ]; then + echo "${BASE_URL}/${REMOTE_BASE}/${REMOTE_PATH}" + return 0 + fi + done + fi + + echo "Could not determine a remote URL for ${COMPONENT} with version ${VERSION}">&2 + exit 1 +} + +mkdir -p ${INSTALL_DIR} +rm -f "${HASH_FILEPATH}" + +for COMPONENT in ${COMPONENTS}; do + + URL="$(compute_url ${COMPONENT})" + echo "Downloading ${COMPONENT} ${URL}..." >&2 + curl --progress-bar -L -o ${DOWNLOAD_DIR}/package.7z ${URL} >&2 + 7z x -y -o${INSTALL_DIR} ${DOWNLOAD_DIR}/package.7z >/dev/null + 7z l -ba -slt -y ${DOWNLOAD_DIR}/package.7z | tr '\\' '/' | sed -n -e "s|^Path\ =\ |${INSTALL_DIR}/|p" >> "${HASH_FILEPATH}" + rm -f ${DOWNLOAD_DIR}/package.7z + + # + # conf file is needed for qmake + # + if [ "${COMPONENT}" == "qtbase" ]; then + if [[ "${TOOLCHAIN}" =~ "win64_mingw" ]]; then + SUBDIR="${TOOLCHAIN/win64_/}_64" + elif [[ "${TOOLCHAIN}" =~ "win32_mingw" ]]; then + SUBDIR="${TOOLCHAIN/win32_/}_32" + elif [[ "${TOOLCHAIN}" =~ "win64_msvc" ]]; then + SUBDIR="${TOOLCHAIN/win64_/}" + elif [[ "${TOOLCHAIN}" =~ "win32_msvc" ]]; then + SUBDIR="${TOOLCHAIN/win32_/}" + elif [[ "${TOOLCHAIN}" =~ "any" ]] && [[ "${TARGET_PLATFORM}" == "android" ]]; then + SUBDIR="android" + else + SUBDIR="${TOOLCHAIN}" + fi + + if [ "${TARGET_PLATFORM}" == "android" ] && [ ! "${QT_VERSION}" \< "6.0.0" ]; then + CONF_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/bin/target_qt.conf" + sed -i "s|target|../$TOOLCHAIN|g" "${CONF_FILE}" + sed -i "/HostPrefix/ s|$|gcc_64|g" "${CONF_FILE}" + ANDROID_QMAKE_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/bin/qmake" + QMAKE_FILE="${INSTALL_DIR}/${VERSION}/gcc_64/bin/qmake" + sed -i "s|\/home\/qt\/work\/install\/bin\/qmake|$QMAKE_FILE|g" "${ANDROID_QMAKE_FILE}" + else + CONF_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/bin/qt.conf" + echo "[Paths]" > ${CONF_FILE} + echo "Prefix = .." >> ${CONF_FILE} + fi + + # Adjust the license to be able to run qmake + # sed with -i requires intermediate file on Mac OS + PRI_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/mkspecs/qconfig.pri" + sed -i.bak 's/Enterprise/OpenSource/g' "${PRI_FILE}" + sed -i.bak 's/licheck.*//g' "${PRI_FILE}" + rm "${PRI_FILE}.bak" + + # Print the directory so that the caller can + # adjust the PATH variable. + echo $(dirname "${CONF_FILE}") + elif [[ "${COMPONENT}" =~ "qtcreator" ]]; then + if [ "${HOST_OS}" == "mac_x64" ]; then + echo "${INSTALL_DIR}/Qt Creator.app/Contents/MacOS" + else + echo "${INSTALL_DIR}/bin" + fi + fi + +done diff --git a/deploy/macos.sh b/deploy/macos.sh index efe505a8..5bb14f5d 100755 --- a/deploy/macos.sh +++ b/deploy/macos.sh @@ -44,6 +44,9 @@ cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install. cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME +rm -f $BUNDLE_DIR/Contents/macOS/post_install.sh $BUNDLE_DIR/Contents/macOS/post_uninstall.sh +chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall.sh + cd $BUNDLE_DIR tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./ diff --git a/deploy/windows.bat b/deploy/windows.bat index 1a1bd1d9..8fee7614 100644 --- a/deploy/windows.bat +++ b/deploy/windows.bat @@ -2,8 +2,8 @@ CHCP 1252 -SET QT_BIN_DIR="c:\Devel\Qt\5.14.2\msvc2017\bin" -SET QIF_BIN_DIR="c:\Devel\Qt\Tools\QtInstallerFramework\4.0\bin" +SET QT_BIN_DIR="c:\Qt\5.14.2\msvc2017\bin" +SET QIF_BIN_DIR="c:\Qt\Tools\QtInstallerFramework\4.0\bin" set APP_NAME=AmneziaVPN set APP_FILENAME=%APP_NAME:"=%.exe @@ -45,6 +45,7 @@ del "%OUT_APP_DIR:"=%\*.cpp" del "%OUT_APP_DIR:"=%\*.h" del "%OUT_APP_DIR:"=%\*.res" del "%OUT_APP_DIR:"=%\*.o" +del "%OUT_APP_DIR:"=%\*.moc" del "%OUT_APP_DIR:"=%\*.lib" del "%OUT_APP_DIR:"=%\*.exp" echo "Deploying..." diff --git a/platform/post-uninstall/post-uninstall.pro b/platform/post-uninstall/post-uninstall.pro index a59aa9f8..93ffc8e4 100644 --- a/platform/post-uninstall/post-uninstall.pro +++ b/platform/post-uninstall/post-uninstall.pro @@ -6,11 +6,11 @@ QT = core SOURCES = \ main.cpp -CONFIG(release, debug|release) { - DESTDIR = $$PWD/../../../AmneziaVPN-build/post-uninstall/release - MOC_DIR = $$DESTDIR - OBJECTS_DIR = $$DESTDIR - RCC_DIR = $$DESTDIR -} +#CONFIG(release, debug|release) { +# DESTDIR = $$PWD/../../../AmneziaVPN-build/post-uninstall/release +# MOC_DIR = $$DESTDIR +# OBJECTS_DIR = $$DESTDIR +# RCC_DIR = $$DESTDIR +#} INCLUDEPATH += "$$PWD/../../client" diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index b98a04d8..8e797065 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -6,11 +6,17 @@ #include "localserver.h" #include "utils.h" +#include "router.h" + +#ifdef Q_OS_WIN +#include "tapcontroller_win.h" +#endif + LocalServer::LocalServer(QObject *parent) : QObject(parent), m_clientConnection(nullptr), m_clientConnected(false) { - m_server = new QLocalServer(this); + m_server = QSharedPointer(new QLocalServer(this)); m_server->setSocketOptions(QLocalServer::WorldAccessOption); if (!m_server->listen(Utils::serverName())) { @@ -18,7 +24,7 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), return; } - connect(m_server, &QLocalServer::newConnection, this, &LocalServer::onNewConnection); + connect(m_server.data(), &QLocalServer::newConnection, this, &LocalServer::onNewConnection); qDebug().noquote() << QString("Local server started on '%1'").arg(m_server->serverName()); } @@ -68,9 +74,16 @@ void LocalServer::onNewConnection() qWarning().noquote() << "Message is not valid!"; continue; } + else { + qDebug().noquote() << QString("Got message id: '%1'").arg(static_cast(incomingMessage.state())); + //qDebug().noquote() << incomingMessage.rawData(); + } switch (incomingMessage.state()) { case Message::State::Initialize: + #ifdef Q_OS_WIN + TapController::Instance().checkAndSetup(); + #endif sendMessage(Message(Message::State::Initialize, QStringList({"Server"}))); break; case Message::State::StartRequest: @@ -79,6 +92,23 @@ void LocalServer::onNewConnection() case Message::State::FinishRequest: finishProcess(incomingMessage.args()); break; + + case Message::State::RoutesAddRequest: + routesAddRequest(incomingMessage.args()); + break; + case Message::State::RouteDeleteRequest: + routeDeleteRequest(incomingMessage.args()); + break; + case Message::State::ClearSavedRoutesRequest: + Router::Instance().clearSavedRoutes(); + break; + case Message::State::FlushDnsRequest: + Router::Instance().flushDns(); + break; + case Message::State::InstallDriverRequest: + checkAndInstallDriver(incomingMessage.args()); + break; + default: ; } @@ -128,6 +158,21 @@ void LocalServer::startProcess(const QStringList& messageArgs) m_processList.append(process); } +void LocalServer::routesAddRequest(const QStringList &messageArgs) +{ + Router::Instance().routeAddList(messageArgs.first(), messageArgs.mid(1)); +} + +void LocalServer::routeDeleteRequest(const QStringList &messageArgs) +{ + Router::Instance().routeDelete(messageArgs.first()); +} + +void LocalServer::checkAndInstallDriver(const QStringList &messageArgs) +{ + +} + void LocalServer::onFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus) diff --git a/service/server/localserver.h b/service/server/localserver.h index d68d121e..68448004 100644 --- a/service/server/localserver.h +++ b/service/server/localserver.h @@ -2,7 +2,9 @@ #define LOCALSERVER_H #include +#include #include +#include #include #include @@ -34,8 +36,14 @@ private: void sendMessage(const Message& message); void startProcess(const QStringList& messageArgs); - QLocalServer* m_server; - QLocalSocket* m_clientConnection; + void routesAddRequest(const QStringList& messageArgs); + void routeDeleteRequest(const QStringList& messageArgs); + + void checkAndInstallDriver(const QStringList& messageArgs); + + QSharedPointer m_server; + QPointer m_clientConnection; + QVector m_processList; bool m_clientConnected; }; diff --git a/client/core/router.cpp b/service/server/router.cpp similarity index 91% rename from client/core/router.cpp rename to service/server/router.cpp index 0c230879..f642be23 100644 --- a/client/core/router.cpp +++ b/service/server/router.cpp @@ -11,13 +11,12 @@ Router &Router::Instance() bool Router::routeAdd(const QString &ip, const QString &gw, QString mask) { -#ifdef Q_OS_WIN - 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"; @@ -108,7 +107,9 @@ bool Router::routeAdd(const QString &ip, const QString &gw, QString mask) free(pIpForwardTable); return (dwStatus == NO_ERROR); - +#else + // Not implemented yet + return false; #endif } @@ -227,9 +228,11 @@ int Router::routeAddList(const QString &gw, const QStringList &ips) if (pIpForwardTable) free(pIpForwardTable); - qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size(); + qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size(); return success_count; - +#else + // Not implemented yet + return false; #endif } @@ -260,7 +263,7 @@ bool Router::clearSavedRoutes() } if (dwStatus != ERROR_SUCCESS) { - qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed"; + qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed"; if (pIpForwardTable) free(pIpForwardTable); return false; @@ -283,24 +286,31 @@ bool Router::clearSavedRoutes() ipForwardRows.clear(); return true; - +#else + // Not implemented yet + return false; #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); + 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(); + p.start(command); + p.waitForFinished(); + qDebug().noquote() << "OUTPUT route delete: " + p.readAll(); - return true; -#endif + return true; +#else + // Not implemented yet + return false; +#endif } void Router::flushDns() @@ -312,6 +322,6 @@ void Router::flushDns() p.start(command); p.waitForFinished(); - qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll(); + //qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll(); #endif } diff --git a/client/core/router.h b/service/server/router.h similarity index 100% rename from client/core/router.h rename to service/server/router.h diff --git a/service/server/server.pro b/service/server/server.pro index 9b35d7af..289b0ec4 100644 --- a/service/server/server.pro +++ b/service/server/server.pro @@ -8,6 +8,7 @@ HEADERS = \ ../../client/utils.h \ localserver.h \ log.h \ + router.h \ systemservice.h SOURCES = \ @@ -16,15 +17,33 @@ SOURCES = \ localserver.cpp \ log.cpp \ main.cpp \ + router.cpp \ systemservice.cpp +win32 { +HEADERS += \ + tapcontroller_win.h + +SOURCES += \ + tapcontroller_win.cpp + +LIBS += \ + -luser32 \ + -lrasapi32 \ + -lshlwapi \ + -liphlpapi \ + -lws2_32 \ + -liphlpapi \ + -lgdi32 +} + include(../src/qtservice.pri) -CONFIG(release, debug|release) { - DESTDIR = $$PWD/../../../AmneziaVPN-build/server/release - MOC_DIR = $$DESTDIR - OBJECTS_DIR = $$DESTDIR - RCC_DIR = $$DESTDIR -} +#CONFIG(release, debug|release) { +# DESTDIR = $$PWD/../../../AmneziaVPN-build/server/release +# MOC_DIR = $$DESTDIR +# OBJECTS_DIR = $$DESTDIR +# RCC_DIR = $$DESTDIR +#} INCLUDEPATH += "$$PWD/../../client" diff --git a/service/server/tapcontroller_win.cpp b/service/server/tapcontroller_win.cpp new file mode 100644 index 00000000..2c553fcf --- /dev/null +++ b/service/server/tapcontroller_win.cpp @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include + +#include "tapcontroller_win.h" + +#define TAP_EXE_ERROR { \ + qDebug() << "TapController: Can't start tapinstall.exe"; \ + return false; \ + } + +#define TAP_NO_MATCHING_DEVICES_ERROR { \ + qDebug() << "TapController: No matching devices found"; \ + return false; \ + } + +TapController &TapController::Instance() +{ + static TapController s; + return s; +} + +TapController::TapController() +{ +} + +bool TapController::checkInstaller() +{ + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "/?"); + if(!tapInstallProc.waitForStarted()) return false; + else return true; +} + +bool TapController::enableTapAdapter(const QString &tapInstanceId) +{ + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "enable" << QString("@") + tapInstanceId); + if(!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + + tapInstallProc.waitForFinished(); + QString output = QString(tapInstallProc.readAll()); + if (! output.contains("are enabled")) { + qDebug() << "TapController: Failed to enable tap device"; + return false; + } + if (output.contains("No matching devices ")) TAP_NO_MATCHING_DEVICES_ERROR ; + + qDebug() << "Enabled TAP Instance id:" << tapInstanceId; + return true; +} + +bool TapController::disableTapAdapter(const QString &tapInstanceId) +{ + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "disable" << QString("@") + tapInstanceId); + if(!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + + tapInstallProc.waitForFinished(); + QString output = QString(tapInstallProc.readAll()); + if (! output.contains("disabled")) { + qDebug() << "TapController: Failed to disable tap device"; + return false; + } + if (output.contains("No matching devices ")) TAP_NO_MATCHING_DEVICES_ERROR ; + + qDebug() << "Disabled TAP Instance id:" << tapInstanceId; + return true; +} + +QStringList TapController::getTapList() +{ + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "find" << "tap0901" ); + if(!tapInstallProc.waitForStarted()) { + qDebug() << "TapController: TapController: Can't start tapinstall.exe"; + return QStringList(); + } + tapInstallProc.waitForFinished(); + QString output = QString( tapInstallProc.readAll() ); + output.replace("\r", ""); + if (output.contains("No matched devices found")) { + qDebug() << "TapController: No matching device instances found"; + return QStringList(); + } + + QStringList l = output.split("\n", QString::SkipEmptyParts); + if (l.size() > 0) l.removeLast(); + + QStringList tapList; + for (QString s : l) { + if (s.contains(" ")) tapList.append(s.split(" ", QString::SkipEmptyParts).first()); + else tapList.append(s); + } + + return tapList; +} + +bool TapController::checkAndSetup() +{ + qDebug().noquote() << "OpenVPN path" << getOpenVpnPath(); + qDebug().noquote() << "TapInstall path" << getTapInstallPath(); + qDebug().noquote() << "TapDriverDir path" << getTapDriverDir(); + ////////////////////////////////////////////// + /// Check if OpenVPN executable ready for use + bool isOpenVpnExeExist = checkOpenVpn(); + if (!isOpenVpnExeExist) { + qDebug() << "TapController::checkAndSetup :::: openvpn.exe not found"; + return false; + } + + //////////////////////////////////////////////// + /// Check if any TAP adapter ready for use + bool isAnyAvailableTap = false; + QStringList tapList = getTapList(); + for (const QString &tap : tapList) { + qDebug() << "TapController: Found TAP device" << tap << ", checking..."; + if (checkDriver(tap)) { + isAnyAvailableTap = true; + qDebug() << "TapController: Device" << tap << "is ready for using"; + } + else + qDebug() << "TapController: Device" << tap << "is NOT ready for using"; + } + + if (isAnyAvailableTap) { + qDebug() << "TapController: Check success, found usable TAP adapter"; + return true; + } + else qDebug() << "TapController: Check failed, usable TAP adapter NOT found"; + + /// Try to setup driver if it's not installed + qDebug() << "TapController: Installing TAP driver..."; + bool ok = setupDriver(); + + if (ok) qDebug() << "TapController: TAP driver successfully installed"; + else qDebug() << "TapController: Failed to install TAP driver"; + + return ok; +} + +bool TapController::checkDriver(const QString& tapInstanceId) +{ + /// Check for driver nodes + { + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "drivernodes" << QString("@") + tapInstanceId); + if (!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + tapInstallProc.waitForFinished(1000); + + QString output = QString(tapInstallProc.readAll()); + if (output.contains("No driver nodes found")) { + qDebug() << "TapController: No driver nodes found"; + return false; + } + if (output.contains("No matching devices")) TAP_NO_MATCHING_DEVICES_ERROR ; + } + + + /// Check for files + { + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "driverfiles" << QString("@") + tapInstanceId); + if (!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + tapInstallProc.waitForFinished(1000); + + QString output = QString(tapInstallProc.readAll()); + if (output.contains("No driver information")) { + qDebug() << "TapController: No driver information"; + return false; + } + if (output.contains("No matching devices")) TAP_NO_MATCHING_DEVICES_ERROR ; + } + + /// Check if network adapter enabled + bool isDisabled = false; + { + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "status" << QString("@") + tapInstanceId); + if(!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + + tapInstallProc.waitForFinished(); + QString output = QString(tapInstallProc.readAll()); + output.replace("\r", ""); + + if (output.contains("No matching devices ")) TAP_NO_MATCHING_DEVICES_ERROR ; + + if (output.contains("is running")) { + qDebug() << "TapController: Device" << tapInstanceId << "is active and ready"; + //return true; + } + else if (output.contains("is disabled")) isDisabled = true; + else { + qDebug() << "TapController: Device" << tapInstanceId << "is in unknown state"; + return false; + } + } + + /// Disable adapter if enabled + if (!isDisabled) { + qDebug() << "TapController: Device" << tapInstanceId << "is enabled. Disabling before use..."; + if (!disableTapAdapter(tapInstanceId)) return false; + } + + /// Enable adapter + { + qDebug() << "TapController: Device" << tapInstanceId << "is disabled. Enabling..."; + if (!enableTapAdapter(tapInstanceId)) return false; + } + + /// Check again + { + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "status" << QString("@") + tapInstanceId); + if(!tapInstallProc.waitForStarted()) TAP_EXE_ERROR ; + + tapInstallProc.waitForFinished(); + QString output = QString(tapInstallProc.readAll()); + + if (output.contains("is running")) return true; + else { + qDebug() << "TapController: tap device final check failed"; + return false; + } + } + +} + +bool TapController::checkOpenVpn() +{ + /// Check openvpn executable + + QProcess openVpnProc; + openVpnProc.start(getOpenVpnPath(), QStringList() << "--version"); + if (!openVpnProc.waitForStarted()) { + qDebug() << "TapController: openvpn.exe NOT found"; + return false; + } + openVpnProc.waitForFinished(1000); + + QString output = QString(openVpnProc.readAll()); + output.replace("\r", ""); + qDebug() << "TapController: openvpn.exe found, version:" << output; + return true; +} + +QString TapController::getTapInstallPath() +{ +#ifdef Q_OS_WIN + return qApp->applicationDirPath() + "\\tap\\"+ QSysInfo::currentCpuArchitecture() + "\\tapinstall.exe"; +// if (QSysInfo::currentCpuArchitecture() == "i386") { +// return qApp->applicationDirPath() + "\\openvpn\\drivers_x32\\tapinstall.exe"; +// } +// else if (QSysInfo::currentCpuArchitecture() == "x86_64") { +// return qApp->applicationDirPath() + "\\openvpn\\drivers_x64\\tapinstall.exe"; +// } +// else return ""; +#endif + return ""; +} + +QString TapController::getOpenVpnPath() +{ +#ifdef Q_OS_WIN + //return qApp->applicationDirPath() + "\\openvpn\\"+ QSysInfo::currentCpuArchitecture() + "\\openvpn.exe"; + return qApp->applicationDirPath() + "\\openvpn\\i386\\openvpn.exe"; + + //return qApp->applicationDirPath() + "\\openvpn\\bin\\openvpn.exe"; +#endif + return ""; +} + +QString TapController::getTapDriverDir() +{ + return qApp->applicationDirPath() + "\\tap\\"+ QSysInfo::currentCpuArchitecture() + "\\"; + +// if (QSysInfo::currentCpuArchitecture() == "i386") { +// return qApp->applicationDirPath() + "\\openvpn\\drivers_x32\\"; +// } +// else if (QSysInfo::currentCpuArchitecture() == "x86_64") { +// return qApp->applicationDirPath() + "\\openvpn\\drivers_x64\\"; +// } +// else return ""; +} + +bool TapController::removeDriver(const QString& tapInstanceId) +{ + /// remove tap by instance id + { + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "remove" << QString("@") + tapInstanceId); + if(!tapInstallProc.waitForStarted()) return false; + + tapInstallProc.waitForFinished(); + QString output = QString( tapInstallProc.readAll() ); + if (output.contains("were removed")) { + qDebug() << "TAP device" << tapInstanceId << "suceccfully removed"; + return true; + } + else { + qDebug() << "Unable to remove TAP device" << tapInstanceId; + return false; + } + } +} + +bool TapController::setupDriver() +{ + //setupDriverCertificate(); + + QStringList tapList = getTapList(); + for (QString tap : tapList) { + if (! checkDriver(tap)) removeDriver(tap); + } + tapList = getTapList(); + if (! tapList.isEmpty()) { + qDebug() << "TapController: setupDriver :::: Found drivers count" << tapList.size(); + return true; + } + + /// else try to install driver + QProcess tapInstallProc; + tapInstallProc.start(getTapInstallPath(), QStringList() << "install" << getTapDriverDir() + "OemVista.inf" << "tap0901"); + bool ok = tapInstallProc.waitForStarted(); + if (!ok) { + qDebug() << "TapController: setupDriver failer to start tapInstallProc" << tapInstallProc.errorString(); + return false; + } + + + tapInstallProc.waitForFinished(); + qDebug() << "TapController: setupDriver args" << tapInstallProc.arguments().join(" "); + qDebug() << "TapController: setupDriver output" << tapInstallProc.readAll(); + + /// check again + tapList = getTapList(); + for (QString tap : tapList) { + if (! checkDriver(tap)) removeDriver(tap); + } + tapList = getTapList(); + if (!tapList.isEmpty()) { + return true; + } + else { + return false; + } + +} + +bool TapController::setupDriverCertificate() +{ + QString cert = getTapDriverDir() + "\\OpenVPN.cer"; + QProcess tapInstallProc; + tapInstallProc.start("certutil" , QStringList() << "-addstore" << "-f" << "trustedpublisher" << cert); + + tapInstallProc.waitForFinished(); + + QString certOutput = QString(tapInstallProc.readAll()); + qDebug() << "TapController: OpenVPN certificate installed:" << certOutput; + return true; +} + diff --git a/service/server/tapcontroller_win.h b/service/server/tapcontroller_win.h new file mode 100644 index 00000000..6e1fb1ba --- /dev/null +++ b/service/server/tapcontroller_win.h @@ -0,0 +1,41 @@ +#ifndef TAPCONTROLLER_H +#define TAPCONTROLLER_H + +#include +#include +#include + +#define IPv6_DEBUG + +//! The TapController class verifies Windows Tap Controller for existance on Windows platform. + +class TapController +{ +public: + static TapController& Instance(); + static bool checkAndSetup(); + static QString getOpenVpnPath(); + + + bool checkInstaller(); + + static QStringList getTapList(); + static bool enableTapAdapter(const QString &tapInstanceId); + static bool disableTapAdapter(const QString &tapInstanceId); +private: + explicit TapController(); + TapController(TapController const &) = delete; + TapController& operator= (TapController const&) = delete; + + static bool checkDriver(const QString& tapInstanceId); + static bool checkOpenVpn(); + static QString getTapInstallPath(); + static QString getTapDriverDir(); + static bool setupDriver(); + static bool setupDriverCertificate(); + static bool removeDriver(const QString& tapInstanceId); + + +}; + +#endif // TAPCONTROLLER_H