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 index 0f71f71d..67618ffb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,46 @@ branches: only: - master - dev + - /\d+\.\d+/ jobs: include: + - name: MacOS + os: osx + osx_image: xcode12.2 + + env: + - QT_VERSION=5.15.1 + + before_install: + - export CERTIFICATE_P12=deploy/PrivacyTechAppleCert.p12 + - export KEYCHAIN=build.keychain + - security create-keychain -p $MAC_CERT_PW $KEYCHAIN + - security default-keychain -s $KEYCHAIN + - security unlock-keychain -p $MAC_CERT_PW $KEYCHAIN + - security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MAC_CERT_PW -T /usr/bin/codesign + + script: + - | + if [ ! -f $HOME/Qt/$QT_VERSION/clang_64/bin/qmake ]; then \ + brew install p7zip && \ + pip3 install aqtinstall requests py7zr && \ + python3 -m aqt install --outputdir $HOME/Qt $QT_VERSION mac desktop clang_64 -m qtbase && \ + python3 -m aqt tool --outputdir $HOME/Qt mac tools_ifw 4.0.1 qt.tools.ifw.40; + fi + - bash deploy/build_macos.sh + + deploy: + provider: releases + token: $GH_TOKEN + skip_cleanup: true + file: + - "AmneziaVPN.dmg" + on: + tags: true + branch: master + + - name: Windows os: windows @@ -14,8 +51,7 @@ jobs: - PATH=/c/Python39:/c/Python39/Scripts:$PATH before_install: - - choco install python --version 3.9.1 - # - pip3 install --upgrade pip + - 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" @@ -28,13 +64,26 @@ jobs: 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 '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 "cd deploy && windows.bat" >> winbuild.bat + - echo -e "set WIN_CERT_PW=$WIN_CERT_PW" >> winbuild.bat + - echo -e "\r\n" >> winbuild.bat + - echo -e "call deploy\\\build_windows.bat" >> winbuild.bat - cmd //c winbuild.bat + deploy: + provider: releases + token: $GH_TOKEN + skip_cleanup: true + file: + - "AmneziaVPN.exe" + on: + tags: true + branch: master - +deploy: + skip_cleanup: true + 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 @@ -44,5 +93,4 @@ cache: directories: - $HOME/Qt - /C/Qt - - $HOME/Library/Caches/Homebrew -# - /usr/local/Cellar/qt \ No newline at end of file + - $HOME/Library/Caches/Homebrew \ No newline at end of file diff --git a/client/client.pro b/client/client.pro index 73811889..57e4fe91 100644 --- a/client/client.pro +++ b/client/client.pro @@ -14,14 +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 \ + protocols/shadowsocksvpnprotocol.h \ runguard.h \ settings.h \ ui/Controls/SlidingStackedWidget.h \ @@ -34,14 +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 \ + protocols/shadowsocksvpnprotocol.cpp \ runguard.cpp \ settings.cpp \ ui/Controls/SlidingStackedWidget.cpp \ 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/openvpnconfigurator.cpp b/client/core/openvpnconfigurator.cpp index 9e18c028..fcbfbbac 100644 --- a/client/core/openvpnconfigurator.cpp +++ b/client/core/openvpnconfigurator.cpp @@ -190,6 +190,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError; } + ServerController::setupServerFirewall(credentials); + return connData; } diff --git a/client/core/openvpnconfigurator.h b/client/core/openvpnconfigurator.h index dc0d1ec0..182aa7d3 100644 --- a/client/core/openvpnconfigurator.h +++ b/client/core/openvpnconfigurator.h @@ -37,7 +37,6 @@ private: 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 c559a24d..2042d2af 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -54,7 +54,7 @@ ErrorCode ServerController::runScript(DockerContainer container, } QEventLoop wait; - int exitStatus; + int exitStatus = -1; // QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ // qDebug() << "Command started"; @@ -66,22 +66,22 @@ ErrorCode ServerController::runScript(DockerContainer container, 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()) { + if (i < lines.count() && exitStatus < 0) { wait.exec(); } @@ -117,7 +117,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, } QEventLoop wait; - int exitStatus = 0; + int exitStatus = -1; // QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ // qDebug() << "Command started"; @@ -138,11 +138,11 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, }); proc->start(); - wait.exec(); + //wait.exec(); -// if (proc->isRunning()) { -// wait.exec(); -// } + if (exitStatus < 0) { + wait.exec(); + } return fromSshProcessExitStatus(exitStatus); } @@ -176,10 +176,15 @@ QString ServerController::getTextFileFromContainer(DockerContainer container, wait.quit(); }); + QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [&](){ + qDebug() << "ServerController::getTextFileFromContainer proc started"; + exitStatus = -1; + }); + proc->start(); wait.exec(); -// if (proc->isRunning()) { +// if (exitStatus < 0) { // wait.exec(); // } @@ -421,3 +426,12 @@ SshConnection *ServerController::connectToHost(const SshConnectionParameters &ss 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 ad2d7e22..56289b21 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -44,6 +44,7 @@ public: 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(DockerContainer container, 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 c290e13d..37d1b77e 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "communicator.h" @@ -106,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 @@ -209,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")) { @@ -220,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); @@ -259,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 84f7709d..3fe8b21e 100644 --- a/client/protocols/openvpnprotocol.h +++ b/client/protocols/openvpnprotocol.h @@ -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/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 e212d15e..b20355a1 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -39,5 +39,6 @@ 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/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 ad05b708..c9714ac3 100644 --- a/client/server_scripts/setup_openvpn_server.sh +++ b/client/server_scripts/setup_openvpn_server.sh @@ -1,6 +1,9 @@ #CONTAINER_NAME=... this var will be set in ServerController -#apt update +apt-get update + +iptables -P FORWARD ACCEPT + apt install -y docker.io curl systemctl start docker @@ -18,4 +21,4 @@ docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem / 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 -i $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf &" +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 87705aba..6f147298 100644 --- a/client/server_scripts/setup_shadowsocks_server.sh +++ b/client/server_scripts/setup_shadowsocks_server.sh @@ -1,6 +1,9 @@ #CONTAINER_NAME=... this var will be set in ServerController -#apt update +apt-get update + +iptables -P FORWARD ACCEPT + apt install -y docker.io curl systemctl start docker @@ -18,4 +21,4 @@ docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem / 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 -i $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf &" +docker exec -d $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf" 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/mainwindow.cpp b/client/ui/mainwindow.cpp index c5c00f24..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" @@ -33,8 +35,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), #endif ui(new Ui::MainWindow), - m_vpnConnection(nullptr), - m_settings(new Settings) + m_vpnConnection(nullptr) { ui->setupUi(this); ui->label_error_text->clear(); @@ -43,8 +44,6 @@ MainWindow::MainWindow(QWidget *parent) : 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); @@ -53,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); @@ -62,12 +61,20 @@ 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(); - ui->radioButton_mode_all_sites->hide(); - ui->radioButton_mode_include_selected_sites->hide(); - ui->pushButton_blocked_list->hide(); - ui->label_description->hide(); + //ui->pushButton_share_connection->hide(); setFixedSize(width(),height()); @@ -119,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())); } } @@ -241,8 +248,8 @@ void MainWindow::onPushButtonNewServerConnectWithNewData(bool) 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(); @@ -251,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) { @@ -312,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, @@ -323,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" + @@ -342,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); } @@ -483,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))); @@ -491,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); }); @@ -500,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) @@ -564,7 +609,7 @@ void MainWindow::onConnect() 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) { @@ -591,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 ef2215fb..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,6 +89,8 @@ private: QSystemTrayIcon m_tray; QMenu* m_menu; + QStringListModel *customSitesModel = nullptr; + bool canMove = false; QPoint offset; bool eventFilter(QObject *obj, QEvent *event) override; diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index adb10a6f..10ff3837 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -14,8 +14,7 @@ - /*----------------------*/ - + QLabel { color: #181922; } @@ -257,17 +256,10 @@ QPushButton:hover { - QStackedWidget#stackedWidget_main{ - background: transparent; -} - -QStackedWidget QWidget { - background: transparent; -} - + - 2 + 0 @@ -365,7 +357,7 @@ border-radius: 4px; - + 40 @@ -384,6 +376,9 @@ color: #333333; + + vpn://... + @@ -1162,17 +1157,18 @@ color: #181922; true - + - 0 - 500 - 381 - 131 + 10 + 460 + 361 + 141 - image: url(:/images/AmneziaVPN.png); + image: url(:/images/AmneziaVPN.png); +background-color: rgb(255, 255, 255); @@ -1303,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 @@ -1360,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 @@ -1379,10 +1375,10 @@ border: 1px solid #A7A7A7; PointingHandCursor - /* black */ -background: #181922; + background: #100A44; border-radius: 4px; -font-size: 18pt; +font-size: 24px; +color: white + @@ -1407,7 +1403,7 @@ font-size: 18pt; color: #181922; - Delete selected item + Delete selected site @@ -1432,7 +1428,7 @@ line-height: 150%; color: #333333; - Hostname or IP address + Web site or hostname or IP address @@ -1947,6 +1943,9 @@ color: #333333; label_server_settings_server + + + @@ -2013,17 +2012,19 @@ color: #181922; true - + 30 100 320 - 151 + 211 - background: #F5F5F5; + QTextEdit { + +background: #F5F5F5; border-radius: 10px; @@ -2036,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 @@ -2090,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 5da635bc..6e9c0571 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -9,6 +9,7 @@ #include "protocols/shadowsocksvpnprotocol.h" #include "utils.h" #include "vpnconnection.h" +#include "communicator.h" VpnConnection::VpnConnection(QObject* parent) : QObject(parent) { @@ -22,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); } @@ -113,6 +133,9 @@ 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; } diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 3964c40e..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; @@ -41,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 index 664b811c..ecd31833 100644 --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -35,7 +35,7 @@ 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 +QT_BIN_DIR=$HOME/Qt/$QT_VERSION/clang_64/bin #QIF_BIN_DIR=$HOME/Qt/Tools/QtInstallerFramework/4.0/bin QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/4.0/bin diff --git a/deploy/build_windows.bat b/deploy/build_windows.bat new file mode 100644 index 00000000..9d938f5e --- /dev/null +++ b/deploy/build_windows.bat @@ -0,0 +1,102 @@ + @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%" +exit 0 \ No newline at end of file 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 99% rename from client/core/router.cpp rename to service/server/router.cpp index 3127549f..f642be23 100644 --- a/client/core/router.cpp +++ b/service/server/router.cpp @@ -322,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 9ed88d9d..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,8 +17,26 @@ 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) { 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