From d831d68e7310065391b895137741ffa9a6372ecc Mon Sep 17 00:00:00 2001 From: pokamest Date: Thu, 18 Mar 2021 18:45:08 +0300 Subject: [PATCH] ShadowSocks protocol fixes: - remote for OpenVPN is set to real ip address - remote ip will be added as alias in docker container - ss-local graceful shutdown - crash fixes --- client/core/defs.h | 6 +- client/core/openvpnconfigurator.cpp | 9 +-- client/core/servercontroller.cpp | 38 +++++++++--- client/core/servercontroller.h | 5 +- client/managementserver.cpp | 12 ++-- client/protocols/openvpnprotocol.cpp | 7 ++- client/protocols/shadowsocksvpnprotocol.cpp | 53 +++++++++++++---- client/protocols/shadowsocksvpnprotocol.h | 5 +- client/server_scripts/remove_container.sh | 4 +- client/server_scripts/setup_firewall.sh | 6 +- client/server_scripts/setup_openvpn_server.sh | 10 ++-- .../setup_shadowsocks_server.sh | 10 ++-- client/utils.cpp | 59 +++++++++++++++---- client/utils.h | 14 ++++- 14 files changed, 173 insertions(+), 65 deletions(-) diff --git a/client/core/defs.h b/client/core/defs.h index bb154aff..bc6fdd89 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -63,7 +63,11 @@ enum ErrorCode // VPN errors OpenVpnAdaptersInUseError, - OpenVpnUnknownError + OpenVpnUnknownError, + + // 3rd party utils errors + OpenVpnExecutableCrashed, + ShadowSocksExecutableCrashed }; namespace config { diff --git a/client/core/openvpnconfigurator.cpp b/client/core/openvpnconfigurator.cpp index efa860f4..723f719b 100644 --- a/client/core/openvpnconfigurator.cpp +++ b/client/core/openvpnconfigurator.cpp @@ -240,12 +240,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia config.replace("redirect-gateway def1 bypass-dhcp", ""); } - if (proto == Protocol::ShadowSocks) { - config.replace("$REMOTE_HOST", "10.8.0.1"); - } - else { - config.replace("$REMOTE_HOST", connData.host); - } + config.replace("$REMOTE_HOST", connData.host); config.replace("$REMOTE_PORT", "1194"); config.replace("$CA_CERT", connData.caCert); config.replace("$CLIENT_CERT", connData.clientCert); @@ -265,7 +260,9 @@ QString OpenVpnConfigurator::convertOpenSShKey(const QString &key) p.setProcessChannelMode(QProcess::MergedChannels); QTemporaryFile tmp; +#ifdef QT_DEBUG tmp.setAutoRemove(false); +#endif tmp.open(); tmp.write(key.toUtf8()); tmp.close(); diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index bcd06156..4ce35ae2 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -11,6 +11,7 @@ #include #include "sshconnectionmanager.h" +#include "utils.h" using namespace QSsh; @@ -24,7 +25,7 @@ QString ServerController::getContainerName(DockerContainer container) } } -ErrorCode ServerController::runScript(DockerContainer container, +ErrorCode ServerController::runScript(const QHash &vars, const SshConnectionParameters &sshParams, QString script, const std::function)> &cbReadStdOut, const std::function)> &cbReadStdErr) @@ -41,7 +42,11 @@ ErrorCode ServerController::runScript(DockerContainer container, const QStringList &lines = script.split("\n", QString::SkipEmptyParts); for (int i = 0; i < lines.count(); i++) { QString line = lines.at(i); - line.replace("$CONTAINER_NAME", getContainerName(container)); + + for (const QString &var : vars.keys()) { + //qDebug() << "Replacing" << var << vars.value(var); + line.replace(var, vars.value(var)); + } if (line.startsWith("#")) { continue; @@ -209,7 +214,7 @@ ErrorCode ServerController::signCert(DockerContainer container, QStringList script {script_import, script_sign}; - return runScript(container, sshParams(credentials), script.join("\n")); + return runScript(genVarsForScript(credentials, container), sshParams(credentials), script.join("\n")); } ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials) @@ -306,7 +311,7 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P scriptData = file.readAll(); if (scriptData.isEmpty()) return ErrorCode::InternalError; - return runScript(container, sshParams(credentials), scriptData); + return runScript(genVarsForScript(credentials, container), sshParams(credentials), scriptData); } ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Protocol proto) @@ -351,7 +356,7 @@ ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credenti stdOut += data + "\n"; }; - ErrorCode e = runScript(DockerContainer::OpenVpn, sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); + ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::OpenVpn), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); if (e) return e; QApplication::processEvents(); @@ -384,7 +389,7 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred stdOut += data + "\n"; }; - ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); + ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); if (e) return e; // Create ss config @@ -407,10 +412,27 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred QString script = QString("sudo docker exec -d %1 sh -c \"ss-server -c %2\""). arg(getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath); - e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), script); + e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), script); return e; } +QHash ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container) +{ + QHash vars; + + vars.insert("$CONTAINER_NAME", getContainerName(container)); + + QString serverIp = Utils::getIPAddress(credentials.hostName); + if (!serverIp.isEmpty()) { + vars.insert("$SERVER_IP_ADDRESS", serverIp); + } + else { + qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName"; + } + + return vars; +} + SshConnection *ServerController::connectToHost(const SshConnectionParameters &sshParams) { SshConnection *client = acquireConnection(sshParams); @@ -470,5 +492,5 @@ ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credent file.open(QIODevice::ReadOnly); QString script = file.readAll(); - return runScript(DockerContainer::OpenVpn, sshParams(credentials), script); + return runScript(genVarsForScript(credentials, DockerContainer::OpenVpn), sshParams(credentials), script); } diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index ad1b947f..a5a90515 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -47,7 +47,8 @@ public: static ErrorCode setupServerFirewall(const ServerCredentials &credentials); private: static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); - static ErrorCode runScript(DockerContainer container, + + static ErrorCode runScript(const QHash &vars, const QSsh::SshConnectionParameters &sshParams, QString script, const std::function)> &cbReadStdOut = nullptr, const std::function)> &cbReadStdErr = nullptr); @@ -55,6 +56,8 @@ private: static ErrorCode setupOpenVpnServer(const ServerCredentials &credentials); static ErrorCode setupShadowSocksServer(const ServerCredentials &credentials); + + static QHash genVarsForScript(const ServerCredentials &credentials, DockerContainer container); }; #endif // SERVERCONTROLLER_H diff --git a/client/managementserver.cpp b/client/managementserver.cpp index fbc6cbf4..c8e3b967 100644 --- a/client/managementserver.cpp +++ b/client/managementserver.cpp @@ -22,7 +22,7 @@ bool ManagementServer::isOpen() const void ManagementServer::stop() { - m_tcpServer->close(); + if (m_tcpServer) m_tcpServer->close(); } void ManagementServer::onAcceptError(QAbstractSocket::SocketError socketError) @@ -37,7 +37,9 @@ qint64 ManagementServer::writeCommand(const QString& message) } const QString command = message + "\n"; - return m_socket->write(command.toStdString().c_str()); + qint64 bytesWritten = m_socket->write(command.toStdString().c_str()); + m_socket->flush(); + return bytesWritten; } void ManagementServer::onNewConnection() @@ -45,7 +47,7 @@ void ManagementServer::onNewConnection() qDebug() << "New incoming connection"; m_socket = QPointer(m_tcpServer->nextPendingConnection()); - m_tcpServer->close(); + if (m_tcpServer) m_tcpServer->close(); QObject::connect(m_socket.data(), SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); QObject::connect(m_socket.data(), SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); @@ -78,9 +80,7 @@ void ManagementServer::onReadyRead() bool ManagementServer::start(const QString& host, unsigned int port) { - if (m_tcpServer) { - m_tcpServer->deleteLater(); - } + if (m_tcpServer) m_tcpServer->close(); m_tcpServer = QSharedPointer(new QTcpServer(this), [](QTcpServer *s){ if (s) s->deleteLater(); diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index f87b574e..c3fddebe 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -17,12 +17,15 @@ OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject* pare OpenVpnProtocol::~OpenVpnProtocol() { - qDebug() << "OpenVpnProtocol::stop()"; + qDebug() << "OpenVpnProtocol::~OpenVpnProtocol()"; OpenVpnProtocol::stop(); + QThread::msleep(200); } void OpenVpnProtocol::stop() { + qDebug() << "OpenVpnProtocol::stop()"; + // TODO: need refactoring // sendTermSignal() will even return true while server connected ??? if ((m_connectionState == VpnProtocol::ConnectionState::Preparing) || @@ -32,6 +35,8 @@ void OpenVpnProtocol::stop() if (!sendTermSignal()) { killOpenVpnProcess(); } + m_managementServer.stop(); + qApp->processEvents(); setConnectionState(VpnProtocol::ConnectionState::Disconnecting); } } diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 841075d1..8051ae96 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -16,26 +16,51 @@ ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration, ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol() { - qDebug() << "ShadowSocksVpnProtocol::stop()"; + qDebug() << "ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol"; ShadowSocksVpnProtocol::stop(); + QThread::msleep(200); + m_ssProcess.close(); } ErrorCode ShadowSocksVpnProtocol::start() { - qDebug() << "ShadowSocksVpnProtocol::start()"; + if (Utils::processIsRunning(Utils::executable("ss-local", false))) { + Utils::killProcessByName(Utils::executable("ss-local", false)); + } + +#ifdef QT_DEBUG + m_shadowSocksCfgFile.setAutoRemove(false); +#endif + m_shadowSocksCfgFile.open(); + m_shadowSocksCfgFile.write(QJsonDocument(m_shadowSocksConfig).toJson()); + m_shadowSocksCfgFile.close(); + + QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName() + << "--no-delay"; + + qDebug().noquote() << "ShadowSocksVpnProtocol::start()" + << shadowSocksExecPath() << args.join(" "); m_ssProcess.setProcessChannelMode(QProcess::MergedChannels); m_ssProcess.setProgram(shadowSocksExecPath()); - m_ssProcess.setArguments(QStringList() << "-s" << m_shadowSocksConfig.value("server").toString() - << "-p" << QString::number(m_shadowSocksConfig.value("server_port").toInt()) - << "-l" << QString::number(m_shadowSocksConfig.value("local_port").toInt()) - << "-m" << m_shadowSocksConfig.value("method").toString() - << "-k" << m_shadowSocksConfig.value("password").toString() - ); + m_ssProcess.setArguments(args); - connect(&m_ssProcess, &QProcess::readyRead, this, [this](){ - qDebug().noquote() << m_ssProcess.readAll(); + connect(&m_ssProcess, &QProcess::readyReadStandardOutput, this, [this](){ + qDebug().noquote() << "ss-local:" << m_ssProcess.readAllStandardOutput(); + }); + + connect(&m_ssProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){ + qDebug().noquote() << "ShadowSocksVpnProtocol finished, exitCode, exiStatus" << exitCode << exitStatus; + setConnectionState(VpnProtocol::ConnectionState::Disconnected); + if (exitStatus != QProcess::NormalExit){ + emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed); + stop(); + } + if (exitCode !=0 ){ + emit protocolError(amnezia::ErrorCode::InternalError); + stop(); + } }); m_ssProcess.start(); @@ -54,10 +79,14 @@ void ShadowSocksVpnProtocol::stop() OpenVpnProtocol::stop(); qDebug() << "ShadowSocksVpnProtocol::stop()"; - m_ssProcess.close(); + m_ssProcess.terminate(); + +#ifdef Q_OS_WIN + Utils::signalCtrl(m_ssProcess.processId(), CTRL_C_EVENT); +#endif } -QString ShadowSocksVpnProtocol::shadowSocksExecPath() const +QString ShadowSocksVpnProtocol::shadowSocksExecPath() { #ifdef Q_OS_WIN return Utils::executable(QString("ss/ss-local"), true); diff --git a/client/protocols/shadowsocksvpnprotocol.h b/client/protocols/shadowsocksvpnprotocol.h index 7462aa4c..86ad40b3 100644 --- a/client/protocols/shadowsocksvpnprotocol.h +++ b/client/protocols/shadowsocksvpnprotocol.h @@ -17,13 +17,16 @@ public: protected: void readShadowSocksConfiguration(const QJsonObject &configuration); - QString shadowSocksExecPath() const; protected: QJsonObject m_shadowSocksConfig; +private: + static QString shadowSocksExecPath(); + private: QProcess m_ssProcess; + QTemporaryFile m_shadowSocksCfgFile; }; #endif // SHADOWSOCKSVPNPROTOCOL_H diff --git a/client/server_scripts/remove_container.sh b/client/server_scripts/remove_container.sh index e5dbd439..89ad6cce 100644 --- a/client/server_scripts/remove_container.sh +++ b/client/server_scripts/remove_container.sh @@ -1,2 +1,2 @@ -docker stop $CONTAINER_NAME -docker rm -f $CONTAINER_NAME +sudo docker stop $CONTAINER_NAME +sudo docker rm -f $CONTAINER_NAME diff --git a/client/server_scripts/setup_firewall.sh b/client/server_scripts/setup_firewall.sh index 706a7a44..dfd570f5 100644 --- a/client/server_scripts/setup_firewall.sh +++ b/client/server_scripts/setup_firewall.sh @@ -1,3 +1,3 @@ -sysctl -w net.ipv4.ip_forward=1 -iptables -P FORWARD ACCEPT -iptables -A INPUT -p icmp --icmp-type echo-request -j DROP +sudo sysctl -w net.ipv4.ip_forward=1 +sudo iptables -P FORWARD ACCEPT +sudo 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 5ed1b0c4..2b0648e2 100644 --- a/client/server_scripts/setup_openvpn_server.sh +++ b/client/server_scripts/setup_openvpn_server.sh @@ -1,12 +1,12 @@ # CONTAINER_NAME=... this var will be set in ServerController # Don't run commands in background like sh -c "openvpn &" -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; if [[ -f "/usr/bin/sudo" ]]; then $pm install -y sudo; fi +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; if [[ -f "/usr/bin/sudo" ]]; then $pm install -y -q sudo; fi sudo iptables -P FORWARD ACCEPT -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm update -y -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm install -y curl -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then sudo $pm_apt install -y docker.io; else sudo $pm_yum install -y docker; fi +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm update -y -q +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm install -y -q curl +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then sudo export DEBIAN_FRONTEND=noninteractive; sudo $pm_apt install -y -q docker.io; else sudo $pm_yum install -y -q docker; fi sudo systemctl start docker sudo docker stop $CONTAINER_NAME @@ -14,6 +14,8 @@ sudo docker rm -f $CONTAINER_NAME sudo docker pull amneziavpn/openvpn:latest sudo docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/udp --name $CONTAINER_NAME amneziavpn/openvpn:latest +# Prevent to route packets outside of the container in case if server behind of the NAT +sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up" sudo docker exec -i $CONTAINER_NAME sh -c "mkdir -p /opt/amneziavpn_data/clients" sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" diff --git a/client/server_scripts/setup_shadowsocks_server.sh b/client/server_scripts/setup_shadowsocks_server.sh index 105a99ce..0e5964b0 100644 --- a/client/server_scripts/setup_shadowsocks_server.sh +++ b/client/server_scripts/setup_shadowsocks_server.sh @@ -1,12 +1,12 @@ # CONTAINER_NAME=... this var will be set in ServerController # Don't run commands in background like sh -c "openvpn &" -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; if [[ -f "/usr/bin/sudo" ]]; then $pm install -y sudo; fi +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; if [[ -f "/usr/bin/sudo" ]]; then $pm install -y -q sudo; fi sudo iptables -P FORWARD ACCEPT -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm update -y -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm install -y curl -pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then sudo $pm_apt install -y docker.io; else sudo $pm_yum install -y docker; fi +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm update -y -q +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm install -y -q curl +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then sudo export DEBIAN_FRONTEND=noninteractive; sudo $pm_apt install -y -q docker.io; else sudo $pm_yum install -y -q docker; fi sudo systemctl start docker sudo docker stop $CONTAINER_NAME @@ -14,6 +14,8 @@ sudo docker rm -f $CONTAINER_NAME sudo docker pull amneziavpn/shadowsocks:latest sudo docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/tcp -p 6789:6789/tcp --name $CONTAINER_NAME amneziavpn/shadowsocks:latest +# Prevent to route packets outside of the container in case if server behind of the NAT +sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up" sudo docker exec -i $CONTAINER_NAME sh -c "mkdir -p /opt/amneziavpn_data/clients" sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" diff --git a/client/utils.cpp b/client/utils.cpp index c8038f4a..a7d36f6d 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -9,19 +9,6 @@ #include "defines.h" #include "utils.h" -QString Utils::toString(bool value) -{ - return value ? "true" : "false"; -} - -QString Utils::serverName() -{ -#ifdef Q_OS_WIN - return SERVICE_NAME; -#else - return QString("/tmp/%1").arg(SERVICE_NAME); -#endif -} QString Utils::defaultVpnConfigFileName() { @@ -153,3 +140,49 @@ bool Utils::checkIPFormat(const QString& ip) } return true; } + +void Utils::killProcessByName(const QString &name) +{ + qDebug().noquote() << "Kill process" << name; +#ifdef Q_OS_WIN + QProcess::execute(QString("taskkill /im %1 /f").arg(name)); +#else + QProcess::execute(QString("pkill %1").arg(name)); +#endif +} + +#ifdef Q_OS_WIN +// Inspired from http://stackoverflow.com/a/15281070/1529139 +// and http://stackoverflow.com/q/40059902/1529139 +bool Utils::signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) +{ + bool success = false; + DWORD thisConsoleId = GetCurrentProcessId(); + // Leave current console if it exists + // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) + bool consoleDetached = (FreeConsole() != FALSE); + + if (AttachConsole(dwProcessId) != FALSE) + { + // Add a fake Ctrl-C handler for avoid instant kill is this console + // WARNING: do not revert it or current program will be also killed + SetConsoleCtrlHandler(nullptr, true); + success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE); + FreeConsole(); + } + + if (consoleDetached) + { + // Create a new console if previous was deleted by OS + if (AttachConsole(thisConsoleId) == FALSE) + { + int errorCode = GetLastError(); + if (errorCode == 31) // 31=ERROR_GEN_FAILURE + { + AllocConsole(); + } + } + } + return success; +} +#endif diff --git a/client/utils.h b/client/utils.h index fa22c4e6..3cef180c 100644 --- a/client/utils.h +++ b/client/utils.h @@ -4,18 +4,19 @@ #include #include +#ifdef Q_OS_WIN +#include "Windows.h" +#endif + class Utils { public: static QString configPath(); static QString defaultVpnConfigFileName(); static QString executable(const QString& baseName, bool absPath); - static QString serverName(); static QString systemLogPath(); - static QString toString(bool value); 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); @@ -23,6 +24,13 @@ public: static QRegExp ipAddressRegExp() { return QRegExp("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$"); } static QRegExp ipAddressPortRegExp() { return QRegExp("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}" "(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(\\:[0-9]{1,5}){0,1}$"); } + + static bool processIsRunning(const QString& fileName); + static void killProcessByName(const QString &name); + +#ifdef Q_OS_WIN + static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent); +#endif }; #endif // UTILS_H