diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1085d4dc..9add68a1 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -235,6 +235,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h + ${CMAKE_CURRENT_LIST_DIR}/protocols/v2rayprotocol.h ) set(SOURCES ${SOURCES} @@ -245,6 +246,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp + ${CMAKE_CURRENT_LIST_DIR}/protocols/v2rayprotocol.cpp ) endif() @@ -558,7 +560,7 @@ if(WIN32) set(DEPLOY_ARTIFACT_PATH "windows/x32") endif() elseif(LINUX) - set(DEPLOY_ARTIFACT_PATH "linux/client") + set(DEPLOY_ARTIFACT_PATH "linux/client/bin") elseif(APPLE AND NOT IOS) set(DEPLOY_ARTIFACT_PATH "macos") endif() diff --git a/client/configurators/cloak_configurator.cpp b/client/configurators/cloak_configurator.cpp index 6206d141..2e0ee52a 100644 --- a/client/configurators/cloak_configurator.cpp +++ b/client/configurators/cloak_configurator.cpp @@ -7,24 +7,24 @@ #include "core/servercontroller.h" #include "containers/containers_defs.h" -CloakConfigurator::CloakConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +CloakConfigurator::CloakConfigurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } -QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) { ErrorCode e = ErrorCode::NoError; ServerController serverController(m_settings); + QString cloakPublicKey = serverController.getTextFileFromContainer(container, credentials, - amnezia::protocols::cloak::ckPublicKeyPath, &e); + amnezia::protocols::cloak::ckPublicKeyPath, &e); cloakPublicKey.replace("\n", ""); QString cloakBypassUid = serverController.getTextFileFromContainer(container, credentials, - amnezia::protocols::cloak::ckBypassUidKeyPath, &e); + amnezia::protocols::cloak::ckBypassUidKeyPath, &e); cloakBypassUid.replace("\n", ""); if (e) { diff --git a/client/configurators/cloak_configurator.h b/client/configurators/cloak_configurator.h index cf6cd002..cf240ac0 100644 --- a/client/configurators/cloak_configurator.h +++ b/client/configurators/cloak_configurator.h @@ -14,7 +14,7 @@ public: CloakConfigurator(std::shared_ptr settings, QObject *parent = nullptr); QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); }; #endif // CLOAK_CONFIGURATOR_H diff --git a/client/configurators/configurator_base.cpp b/client/configurators/configurator_base.cpp index c24d7e1f..5e5c887d 100644 --- a/client/configurators/configurator_base.cpp +++ b/client/configurators/configurator_base.cpp @@ -1,8 +1,6 @@ #include "configurator_base.h" -ConfiguratorBase::ConfiguratorBase(std::shared_ptr settings, QObject *parent) - : QObject{parent}, - m_settings(settings) +ConfiguratorBase::ConfiguratorBase(std::shared_ptr settings, + QObject *parent): QObject{parent}, m_settings(settings) { - } diff --git a/client/configurators/ikev2_configurator.cpp b/client/configurators/ikev2_configurator.cpp index 7ed83da1..f9059fba 100644 --- a/client/configurators/ikev2_configurator.cpp +++ b/client/configurators/ikev2_configurator.cpp @@ -1,4 +1,5 @@ #include "ikev2_configurator.h" + #include #include #include @@ -15,14 +16,13 @@ #include "core/servercontroller.h" -Ikev2Configurator::Ikev2Configurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +Ikev2Configurator::Ikev2Configurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, - DockerContainer container, ErrorCode *errorCode) + DockerContainer container, ErrorCode *errorCode) { Ikev2Configurator::ConnectionData connData; connData.host = credentials.hostName; @@ -33,21 +33,18 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12"; QString scriptCreateCert = QString("certutil -z <(head -c 1024 /dev/urandom) "\ - "-S -c \"IKEv2 VPN CA\" -n \"%1\" "\ - "-s \"O=IKEv2 VPN,CN=%1\" "\ - "-k rsa -g 3072 -v 120 "\ - "-d sql:/etc/ipsec.d -t \",,\" "\ - "--keyUsage digitalSignature,keyEncipherment "\ - "--extKeyUsage serverAuth,clientAuth -8 \"%1\"") - .arg(connData.clientId); + "-S -c \"IKEv2 VPN CA\" -n \"%1\" "\ + "-s \"O=IKEv2 VPN,CN=%1\" "\ + "-k rsa -g 3072 -v 120 "\ + "-d sql:/etc/ipsec.d -t \",,\" "\ + "--keyUsage digitalSignature,keyEncipherment "\ + "--extKeyUsage serverAuth,clientAuth -8 \"%1\"").arg(connData.clientId); ServerController serverController(m_settings); ErrorCode e = serverController.runContainerScript(credentials, container, scriptCreateCert); QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"") - .arg(connData.password) - .arg(connData.clientId) - .arg(certFileName); + .arg(connData.password, connData.clientId, certFileName); e = serverController.runContainerScript(credentials, container, scriptExportCert); connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, &e); @@ -59,8 +56,8 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se return connData; } -QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) { Q_UNUSED(containerConfig) diff --git a/client/configurators/ikev2_configurator.h b/client/configurators/ikev2_configurator.h index a7d379f2..a2ad35bf 100644 --- a/client/configurators/ikev2_configurator.h +++ b/client/configurators/ikev2_configurator.h @@ -22,14 +22,14 @@ public: }; QString genIkev2Config(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); QString genIkev2Config(const ConnectionData &connData); QString genMobileConfig(const ConnectionData &connData); QString genStrongSwanConfig(const ConnectionData &connData); - ConnectionData prepareIkev2Config(const ServerCredentials &credentials, - DockerContainer container, ErrorCode *errorCode = nullptr); + ConnectionData prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, + ErrorCode *errorCode = nullptr); }; #endif // IKEV2_CONFIGURATOR_H diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 456aeb4d..8b2c45b1 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -1,4 +1,5 @@ #include "openvpn_configurator.h" + #include #include #include @@ -19,14 +20,13 @@ #include #include -OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials, - DockerContainer container, ErrorCode *errorCode) + DockerContainer container, ErrorCode *errorCode) { OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest(); connData.host = credentials.hostName; @@ -36,9 +36,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - QString reqFileName = QString("%1/%2.req"). - arg(amnezia::protocols::openvpn::clientsDirPath). - arg(connData.clientId); + QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath, connData.clientId); ServerController serverController(m_settings); ErrorCode e = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName); @@ -53,9 +51,11 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - connData.caCert = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e); + connData.caCert = serverController.getTextFileFromContainer(container, credentials, + amnezia::protocols::openvpn::caCertPath, &e); connData.clientCert = serverController.getTextFileFromContainer(container, credentials, - QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e); + QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath, connData.clientId), + &e); if (e) { if (errorCode) *errorCode = e; @@ -71,12 +71,12 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } -QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) { ServerController serverController(m_settings); QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), - serverController.genVarsForScript(credentials, container, containerConfig)); + serverController.genVarsForScript(credentials, container, containerConfig)); ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode); if (errorCode && *errorCode) { @@ -89,8 +89,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia if (config.contains("$OPENVPN_TA_KEY")) { config.replace("$OPENVPN_TA_KEY", connData.taKey); - } - else { + } else { config.replace("", ""); config.replace("", ""); } @@ -112,8 +111,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig) if (m_settings->routeMode() != Settings::VpnAllSites) { config.replace("redirect-gateway def1 bypass-dhcp", ""); - } - else { + } else { if(!config.contains("redirect-gateway def1 bypass-dhcp")) { config.append("redirect-gateway def1 bypass-dhcp\n"); } @@ -151,23 +149,22 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig) return QJsonDocument(json).toJson(); } -ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, - const ServerCredentials &credentials, QString clientId) +ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId) { QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && " - "easyrsa import-req %2/%3.req %3\"") - .arg(ContainerProps::containerToString(container)) - .arg(amnezia::protocols::openvpn::clientsDirPath) - .arg(clientId); + "easyrsa import-req %2/%3.req %3\"") + .arg(ContainerProps::containerToString(container), + amnezia::protocols::openvpn::clientsDirPath, + clientId); QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && " - "easyrsa sign-req client %2\"") - .arg(ContainerProps::containerToString(container)) - .arg(clientId); + "easyrsa sign-req client %2\"") + .arg(ContainerProps::containerToString(container), clientId); ServerController serverController(m_settings); QStringList scriptList {script_import, script_sign}; - QString script = serverController.replaceVars(scriptList.join("\n"), serverController.genVarsForScript(credentials, container)); + QString script = serverController.replaceVars(scriptList.join("\n"), + serverController.genVarsForScript(credentials, container)); return serverController.runScript(credentials, script); } @@ -177,8 +174,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() ConnectionData connData; connData.clientId = Utils::getRandomString(32); - int ret = 0; - int nVersion = 1; + int ret = 0; + int nVersion = 1; QByteArray clientIdUtf8 = connData.clientId.toUtf8(); @@ -211,7 +208,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() // 4. set public key of x509 req ret = X509_REQ_set_pubkey(x509_req, pKey); - if (ret != 1){ + if (ret != 1) { qWarning() << "Could not set pubkey!"; X509_REQ_free(x509_req); EVP_PKEY_free(pKey); @@ -220,7 +217,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() // 5. set sign key of x509 req ret = X509_REQ_sign(x509_req, pKey, EVP_sha256()); // return x509_req->signature->length - if (ret <= 0){ + if (ret <= 0) { qWarning() << "Could not sign request!"; X509_REQ_free(x509_req); EVP_PKEY_free(pKey); @@ -230,8 +227,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() // save private key BIO * bp_private = BIO_new(BIO_s_mem()); q_check_ptr(bp_private); - if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1) - { + if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1) { qFatal("PEM_write_bio_PrivateKey"); EVP_PKEY_free(pKey); BIO_free_all(bp_private); diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index 3b84e0a0..180c8543 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -24,20 +24,18 @@ public: }; QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); QString processConfigWithLocalSettings(QString jsonConfig); QString processConfigWithExportSettings(QString jsonConfig); - ErrorCode signCert(DockerContainer container, - const ServerCredentials &credentials, QString clientId); + ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId); private: ConnectionData createCertRequest(); - ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, - DockerContainer container, ErrorCode *errorCode = nullptr); - + ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, + ErrorCode *errorCode = nullptr); }; #endif // OPENVPN_CONFIGURATOR_H diff --git a/client/configurators/shadowsocks_configurator.cpp b/client/configurators/shadowsocks_configurator.cpp index a71064c8..69468080 100644 --- a/client/configurators/shadowsocks_configurator.cpp +++ b/client/configurators/shadowsocks_configurator.cpp @@ -5,16 +5,16 @@ #include #include "containers/containers_defs.h" +#include "core/scripts_registry.h" #include "core/servercontroller.h" -ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } -QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) { ErrorCode e = ErrorCode::NoError; ServerController serverController(m_settings); @@ -28,18 +28,12 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c return ""; } - QJsonObject config; - config.insert("server", credentials.hostName); - config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT"); - config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT"); - config.insert("password", ssKey); - config.insert("timeout", 60); - config.insert("method", "$SHADOWSOCKS_CIPHER"); + QString ssClientConfig = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::shadowsocks_client_template, container), + serverController.genVarsForScript(credentials, container, containerConfig)); - - QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(), - serverController.genVarsForScript(credentials, container, containerConfig)); + ssClientConfig.replace("$SHADOWSOCKS_PASSWORD", ssKey); + ssClientConfig = serverController.replaceVars(ssClientConfig, serverController.genVarsForScript(credentials, container, containerConfig)); //qDebug().noquote() << textCfg; - return textCfg; + return ssClientConfig; } diff --git a/client/configurators/shadowsocks_configurator.h b/client/configurators/shadowsocks_configurator.h index b03149eb..2ee06aa3 100644 --- a/client/configurators/shadowsocks_configurator.h +++ b/client/configurators/shadowsocks_configurator.h @@ -13,7 +13,7 @@ public: ShadowSocksConfigurator(std::shared_ptr settings, QObject *parent = nullptr); QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); }; #endif // SHADOWSOCKS_CONFIGURATOR_H diff --git a/client/configurators/ssh_configurator.cpp b/client/configurators/ssh_configurator.cpp index e1435bc3..261ce4df 100644 --- a/client/configurators/ssh_configurator.cpp +++ b/client/configurators/ssh_configurator.cpp @@ -1,4 +1,5 @@ #include "ssh_configurator.h" + #include #include #include @@ -14,11 +15,9 @@ #include "core/server_defs.h" #include "utilities.h" - -SshConfigurator::SshConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +SshConfigurator::SshConfigurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } QString SshConfigurator::convertOpenSShKey(const QString &key) @@ -73,10 +72,8 @@ void SshConfigurator::openSshTerminal(const ServerCredentials &credentials) // todo: connect by key // p->setNativeArguments(QString("%1@%2") // .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password)); - } - else { - p->setNativeArguments(QString("%1@%2 -pw %3") - .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password)); + } else { + p->setNativeArguments(QString("%1@%2 -pw %3").arg(credentials.userName).arg(credentials.hostName, credentials.password)); } #else p->setProgram("/bin/bash"); diff --git a/client/configurators/ssh_configurator.h b/client/configurators/ssh_configurator.h index 2f8cb550..efefa6cb 100644 --- a/client/configurators/ssh_configurator.h +++ b/client/configurators/ssh_configurator.h @@ -16,7 +16,6 @@ public: QProcessEnvironment prepareEnv(); QString convertOpenSShKey(const QString &key); void openSshTerminal(const ServerCredentials &credentials); - }; #endif // SSH_CONFIGURATOR_H diff --git a/client/configurators/v2ray_configurator.cpp b/client/configurators/v2ray_configurator.cpp new file mode 100644 index 00000000..9c8f8d93 --- /dev/null +++ b/client/configurators/v2ray_configurator.cpp @@ -0,0 +1,46 @@ +#include "v2ray_configurator.h" + +#include +#include +#include +#include +#include + +#include "core/servercontroller.h" +#include "core/scripts_registry.h" +#include "containers/containers_defs.h" + +V2RayConfigurator::V2RayConfigurator(std::shared_ptr settings, + QObject *parent) : ConfiguratorBase(settings, parent) +{ +} + +QString V2RayConfigurator::genV2RayConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) +{ + ErrorCode e = ErrorCode::NoError; + + ServerController serverController(m_settings); + QString v2RayVmessClientUuid = serverController.getTextFileFromContainer(container, credentials, + amnezia::protocols::v2ray::v2rayKeyPath, &e); + if (v2RayVmessClientUuid.isEmpty()) { + if (errorCode) *errorCode = ErrorCode::V2RayKeyMissing; + return ""; + } + + v2RayVmessClientUuid.replace("\n", ""); + + if (e) { + if (errorCode) *errorCode = e; + return ""; + } + + QString v2RayClientConfig = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::v2ray_client_template, container), + serverController.genVarsForScript(credentials, container, containerConfig)); + + v2RayClientConfig.replace("$V2RAY_VMESS_CLIENT_UUID", v2RayVmessClientUuid); + v2RayClientConfig = serverController.replaceVars(v2RayClientConfig, + serverController.genVarsForScript(credentials, container, containerConfig)); + + return v2RayClientConfig; +} diff --git a/client/configurators/v2ray_configurator.h b/client/configurators/v2ray_configurator.h new file mode 100644 index 00000000..87d941a0 --- /dev/null +++ b/client/configurators/v2ray_configurator.h @@ -0,0 +1,20 @@ +#ifndef V2RAYCONFIGURATOR_H +#define V2RAYCONFIGURATOR_H + +#include + +#include "configurator_base.h" + +using namespace amnezia; + +class V2RayConfigurator : ConfiguratorBase +{ +public: + V2RayConfigurator(std::shared_ptr settings, QObject *parent = nullptr); + + QString genV2RayConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); +}; + +#endif // V2RAYCONFIGURATOR_H + diff --git a/client/configurators/vpn_configurator.cpp b/client/configurators/vpn_configurator.cpp index ceb6a5a4..ea69dc77 100644 --- a/client/configurators/vpn_configurator.cpp +++ b/client/configurators/vpn_configurator.cpp @@ -5,6 +5,7 @@ #include "wireguard_configurator.h" #include "ikev2_configurator.h" #include "ssh_configurator.h" +#include "v2ray_configurator.h" #include #include @@ -14,8 +15,8 @@ #include "utilities.h" #include "settings.h" -VpnConfigurator::VpnConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +VpnConfigurator::VpnConfigurator(std::shared_ptr settings, + QObject *parent) : ConfiguratorBase(settings, parent) { openVpnConfigurator = std::shared_ptr(new OpenVpnConfigurator(settings, this)); shadowSocksConfigurator = std::shared_ptr(new ShadowSocksConfigurator(settings, this)); @@ -25,25 +26,22 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr settings, QObject *pa sshConfigurator = std::shared_ptr(new SshConfigurator(settings, this)); } -QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode) +QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode) { switch (proto) { case Proto::OpenVpn: return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode); - case Proto::ShadowSocks: return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode); - case Proto::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode); - case Proto::WireGuard: return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode); - case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode); - + case Proto::V2Ray: + return v2RayConfigurator->genV2RayConfig(credentials, container, containerConfig, errorCode); default: return ""; } @@ -62,8 +60,7 @@ QPair VpnConfigurator::getDnsForConfig(int serverIndex) if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) { if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) { dns.first = protocols::dns::amneziaDnsIp; - } - else dns.first = m_settings->primaryDns(); + } else dns.first = m_settings->primaryDns(); } if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) { dns.second = m_settings->secondaryDns(); @@ -73,8 +70,7 @@ QPair VpnConfigurator::getDnsForConfig(int serverIndex) return dns; } -QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container, - Proto proto, QString &config) +QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config) { auto dns = getDnsForConfig(serverIndex); @@ -84,8 +80,7 @@ QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerCo return config; } -QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container, - Proto proto, QString &config) +QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config) { processConfigWithDnsSettings(serverIndex, container, proto, config); @@ -95,8 +90,7 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker return config; } -QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container, - Proto proto, QString &config) +QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config) { processConfigWithDnsSettings(serverIndex, container, proto, config); @@ -107,7 +101,7 @@ QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, Docke } void VpnConfigurator::updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, - const QString &stdOut) + const QString &stdOut) { Proto mainProto = ContainerProps::defaultProtocol(container); diff --git a/client/configurators/vpn_configurator.h b/client/configurators/vpn_configurator.h index 5d0fcfee..7731c521 100644 --- a/client/configurators/vpn_configurator.h +++ b/client/configurators/vpn_configurator.h @@ -13,6 +13,7 @@ class CloakConfigurator; class WireguardConfigurator; class Ikev2Configurator; class SshConfigurator; +class V2RayConfigurator; // Retrieve connection settings from server class VpnConfigurator : ConfiguratorBase @@ -22,7 +23,7 @@ public: VpnConfigurator(std::shared_ptr settings, QObject *parent = nullptr); QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr); QPair getDnsForConfig(int serverIndex); QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); @@ -31,8 +32,7 @@ public: QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); // workaround for containers which is not support normal configaration - void updateContainerConfigAfterInstallation(DockerContainer container, - QJsonObject &containerConfig, const QString &stdOut); + void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, const QString &stdOut); std::shared_ptr openVpnConfigurator; std::shared_ptr shadowSocksConfigurator; @@ -40,6 +40,7 @@ public: std::shared_ptr wireguardConfigurator; std::shared_ptr ikev2Configurator; std::shared_ptr sshConfigurator; + std::shared_ptr v2RayConfigurator; }; #endif // VPN_CONFIGURATOR_H diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index f7ab1657..74bb2079 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -7,13 +7,11 @@ #include #include - #include #include #include #include - #include "containers/containers_defs.h" #include "core/server_defs.h" #include "core/scripts_registry.h" @@ -21,10 +19,9 @@ #include "core/servercontroller.h" #include "settings.h" -WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, QObject *parent): - ConfiguratorBase(settings, parent) +WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, + QObject *parent): ConfiguratorBase(settings, parent) { - } WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() @@ -59,7 +56,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() } WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) + DockerContainer container, + const QJsonObject &containerConfig, + ErrorCode *errorCode) { WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys(); connData.host = credentials.hostName; @@ -95,8 +94,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon // Calc next IP address if (ips.isEmpty()) { nextIpNumber = "2"; - } - else { + } else { int next = ips.last().split(".").last().toInt() + 1; if (next > 254) { if (errorCode) *errorCode = ErrorCode::AddressPoolError; @@ -120,14 +118,16 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon } // Get keys - connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e); + connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, + amnezia::protocols::wireguard::serverPublicKeyPath, &e); connData.serverPubKey.replace("\n", ""); if (e) { if (errorCode) *errorCode = e; return connData; } - connData.pskKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e); + connData.pskKey = serverController.getTextFileFromContainer(container, credentials, + amnezia::protocols::wireguard::serverPskKeyPath, &e); connData.pskKey.replace("\n", ""); if (e) { @@ -136,32 +136,29 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon } // Add client to config - QString configPart = QString( - "[Peer]\n" - "PublicKey = %1\n" - "PresharedKey = %2\n" - "AllowedIPs = %3/32\n\n"). - arg(connData.clientPubKey). - arg(connData.pskKey). - arg(connData.clientIP); + QString configPart = QString("[Peer]\n" + "PublicKey = %1\n" + "PresharedKey = %2\n" + "AllowedIPs = %3/32\n\n").arg(connData.clientPubKey, connData.pskKey, connData.clientIP); e = serverController.uploadTextFileToContainer(container, credentials, configPart, - protocols::wireguard::serverConfigPath, libssh::SftpOverwriteMode::SftpAppendToExisting); + protocols::wireguard::serverConfigPath, + libssh::SftpOverwriteMode::SftpAppendToExisting); if (e) { if (errorCode) *errorCode = e; return connData; } + QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'"; e = serverController.runScript(credentials, - serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'", - serverController.genVarsForScript(credentials, container))); + serverController.replaceVars(script, serverController.genVarsForScript(credentials, container))); return connData; } -QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode) { ServerController serverController(m_settings); QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container), diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h index ab903d29..19f81dc0 100644 --- a/client/configurators/wireguard_configurator.h +++ b/client/configurators/wireguard_configurator.h @@ -23,15 +23,14 @@ public: }; QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); QString processConfigWithLocalSettings(QString config); QString processConfigWithExportSettings(QString config); - private: - ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); ConnectionData genClientKeys(); }; diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index cca77e7d..7183bcfb 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -55,6 +55,9 @@ QVector ContainerProps::protocolsForContainer(amnezia::DockerCon case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */}; + case DockerContainer::V2Ray: + return { Proto::OpenVpn, Proto::V2Ray }; + case DockerContainer::Dns: return { }; @@ -86,6 +89,7 @@ QMap ContainerProps::containerHumanNames() {DockerContainer::Cloak, "OpenVpn over Cloak"}, {DockerContainer::WireGuard, "WireGuard"}, {DockerContainer::Ipsec, QObject::tr("IPsec")}, + {DockerContainer::V2Ray, "V2Ray"}, {DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")}, {DockerContainer::Dns, QObject::tr("DNS Service")}, @@ -103,6 +107,7 @@ QMap ContainerProps::containerDescriptions() "configured with traffic masking by Cloak plugin")}, {DockerContainer::WireGuard, QObject::tr("WireGuard container")}, {DockerContainer::Ipsec, QObject::tr("IPsec container")}, + {DockerContainer::V2Ray, QObject::tr("V2Ray container")}, {DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")}, {DockerContainer::Dns, QObject::tr("DNS Service")}, @@ -120,6 +125,8 @@ amnezia::ServiceType ContainerProps::containerService(DockerContainer c) case DockerContainer::ShadowSocks : return ServiceType::Vpn; case DockerContainer::WireGuard : return ServiceType::Vpn; case DockerContainer::Ipsec : return ServiceType::Vpn; + case DockerContainer::V2Ray : return ServiceType::Vpn; + case DockerContainer::TorWebSite : return ServiceType::Other; case DockerContainer::Dns : return ServiceType::Other; //case DockerContainer::FileShare : return ServiceType::Other; @@ -137,6 +144,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c) case DockerContainer::ShadowSocks : return Proto::ShadowSocks; case DockerContainer::WireGuard : return Proto::WireGuard; case DockerContainer::Ipsec : return Proto::Ikev2; + case DockerContainer::V2Ray : return Proto::V2Ray; case DockerContainer::TorWebSite : return Proto::TorWebSite; case DockerContainer::Dns : return Proto::Dns; diff --git a/client/containers/containers_defs.h b/client/containers/containers_defs.h index 33e9a4ae..5652b53c 100644 --- a/client/containers/containers_defs.h +++ b/client/containers/containers_defs.h @@ -19,6 +19,7 @@ enum DockerContainer { Cloak, WireGuard, Ipsec, + V2Ray, //non-vpn TorWebSite, diff --git a/client/core/defs.h b/client/core/defs.h index bafd088f..fab963ea 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -55,6 +55,7 @@ enum ErrorCode ShadowSocksExecutableMissing, CloakExecutableMissing, AmneziaServiceConnectionFailed, + V2RayExecutableMissing, ExecutableMissing, // VPN errors @@ -67,7 +68,9 @@ enum ErrorCode OpenSslFailed, OpenVpnExecutableCrashed, ShadowSocksExecutableCrashed, - CloakExecutableCrashed + CloakExecutableCrashed, + V2RayExecutableCrashed, + V2RayKeyMissing }; } // namespace amnezia diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 5d2a337b..69f50643 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -51,6 +51,10 @@ QString errorString(ErrorCode code){ case (AmneziaServiceConnectionFailed): return QObject::tr("Amnezia helper service error"); case (OpenSslFailed): return QObject::tr("OpenSSL failed"); + // V2Ray errors + case (V2RayExecutableMissing): return QObject::tr("V2Ray (v2ray) executable missing"); + case (V2RayKeyMissing): return QObject::tr("V2Ray key missing"); + // VPN errors case (OpenVpnAdaptersInUseError): return QObject::tr("Can't connect: another VPN connection is active"); case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter"); diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp index d33881a2..8fa70c9a 100644 --- a/client/core/scripts_registry.cpp +++ b/client/core/scripts_registry.cpp @@ -12,6 +12,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container) case DockerContainer::ShadowSocks: return QLatin1String("openvpn_shadowsocks"); case DockerContainer::WireGuard: return QLatin1String("wireguard"); case DockerContainer::Ipsec: return QLatin1String("ipsec"); + case DockerContainer::V2Ray: return QLatin1String("openvpn_v2ray_vmess"); case DockerContainer::TorWebSite: return QLatin1String("website_tor"); case DockerContainer::Dns: return QLatin1String("dns"); @@ -44,6 +45,8 @@ QString amnezia::scriptName(ProtocolScriptType type) case ProtocolScriptType::container_startup: return QLatin1String("start.sh"); case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn"); case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf"); + case ProtocolScriptType::v2ray_client_template: return QLatin1String("template_v2ray_client.json"); + case ProtocolScriptType::shadowsocks_client_template: return QLatin1String("template_ss_client.json"); } } diff --git a/client/core/scripts_registry.h b/client/core/scripts_registry.h index 9528d763..9887d5c4 100644 --- a/client/core/scripts_registry.h +++ b/client/core/scripts_registry.h @@ -25,7 +25,9 @@ enum ProtocolScriptType { configure_container, container_startup, openvpn_template, - wireguard_template + wireguard_template, + v2ray_client_template, + shadowsocks_client_template }; diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index ae35efa5..43594298 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -516,6 +516,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject(); const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject(); const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject(); + const QJsonObject &v2RayConfig = config.value(ProtocolProps::protoToString(Proto::V2Ray)).toObject(); // Vars vars; @@ -567,6 +568,10 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }}); + // V2Ray vars + vars.append({{"$V2RAY_VMESS_PORT", v2RayConfig.value(config_key::port).toString(protocols::v2ray::defaultServerPort) }}); + vars.append({{"$V2RAY_SOCKS_LOCAL_PORT", v2RayConfig.value(config_key::local_port).toString(protocols::v2ray::defaultLocalPort) }}); + // IPsec vars vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}}); vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}}); diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp index 55939895..59152f57 100644 --- a/client/protocols/openvpnovercloakprotocol.cpp +++ b/client/protocols/openvpnovercloakprotocol.cpp @@ -52,26 +52,25 @@ ErrorCode OpenVpnOverCloakProtocol::start() args << "-u"; } - qDebug().noquote() << "OpenVpnOverCloakProtocol::start()" - << cloakExecPath() << args.join(" "); + qDebug().noquote() << "OpenVpnOverCloakProtocol::start()" << cloakExecPath() << args.join(" "); m_ckProcess.setProcessChannelMode(QProcess::MergedChannels); m_ckProcess.setProgram(cloakExecPath()); m_ckProcess.setArguments(args); - connect(&m_ckProcess, &QProcess::readyReadStandardOutput, this, [this](){ + connect(&m_ckProcess, &QProcess::readyReadStandardOutput, this, [this]() { qDebug().noquote() << "ck-client:" << m_ckProcess.readAllStandardOutput(); }); - m_errorHandlerConnection = connect(&m_ckProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){ + m_errorHandlerConnection = connect(&m_ckProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { qDebug().noquote() << "OpenVpnOverCloakProtocol finished, exitCode, exiStatus" << exitCode << exitStatus; setConnectionState(VpnProtocol::Disconnected); - if (exitStatus != QProcess::NormalExit){ + if (exitStatus != QProcess::NormalExit) { emit protocolError(amnezia::ErrorCode::CloakExecutableCrashed); stop(); } - if (exitCode !=0 ){ + if (exitCode !=0 ) { emit protocolError(amnezia::ErrorCode::InternalError); stop(); } @@ -84,8 +83,9 @@ ErrorCode OpenVpnOverCloakProtocol::start() setConnectionState(VpnConnectionState::Connecting); return OpenVpnProtocol::start(); + } else { + return ErrorCode::CloakExecutableMissing; } - else return ErrorCode::CloakExecutableMissing; #endif } diff --git a/client/protocols/protocols_defs.cpp b/client/protocols/protocols_defs.cpp index 549c3774..e730f53b 100644 --- a/client/protocols/protocols_defs.cpp +++ b/client/protocols/protocols_defs.cpp @@ -75,6 +75,7 @@ QMap ProtocolProps::protocolHumanNames() {Proto::WireGuard, "WireGuard"}, {Proto::Ikev2, "IKEv2"}, {Proto::L2tp, "L2TP"}, + {Proto::V2Ray, "V2Ray"}, {Proto::TorWebSite, "Web site in Tor network"}, {Proto::Dns, "DNS Service"}, @@ -96,10 +97,12 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p) case Proto::Cloak : return ServiceType::Vpn; case Proto::ShadowSocks : return ServiceType::Vpn; case Proto::WireGuard : return ServiceType::Vpn; - case Proto::TorWebSite : return ServiceType::Other; + case Proto::V2Ray : return ServiceType::Vpn; + + case Proto::TorWebSite : return ServiceType::Other; case Proto::Dns : return ServiceType::Other; case Proto::FileShare : return ServiceType::Other; - default: return ServiceType::Other; + default: return ServiceType::Other; } } @@ -113,12 +116,13 @@ int ProtocolProps::defaultPort(Proto p) case Proto::WireGuard : return 51820; case Proto::Ikev2 : return -1; case Proto::L2tp : return -1; + case Proto::V2Ray : return 10086; case Proto::TorWebSite : return -1; case Proto::Dns : return 53; case Proto::FileShare : return 139; case Proto::Sftp : return 222; - default: return -1; + default: return -1; } } @@ -132,12 +136,13 @@ bool ProtocolProps::defaultPortChangeable(Proto p) case Proto::WireGuard : return true; case Proto::Ikev2 : return false; case Proto::L2tp : return false; + case Proto::V2Ray : return true; case Proto::TorWebSite : return true; case Proto::Dns : return false; case Proto::FileShare : return false; - default: return -1; + default: return -1; } } @@ -151,6 +156,7 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p) case Proto::WireGuard : return TransportProto::Udp; case Proto::Ikev2 : return TransportProto::Udp; case Proto::L2tp : return TransportProto::Udp; + case Proto::V2Ray : return TransportProto::Tcp; // non-vpn case Proto::TorWebSite : return TransportProto::Tcp; case Proto::Dns : return TransportProto::Udp; @@ -169,12 +175,13 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p) case Proto::WireGuard : return false; case Proto::Ikev2 : return false; case Proto::L2tp : return false; + case Proto::V2Ray : return false; // non-vpn case Proto::TorWebSite : return false; case Proto::Dns : return false; case Proto::FileShare : return false; case Proto::Sftp : return false; - default: return false; + default: return false; } } diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index c5f15d5b..d25d85d1 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -124,6 +124,12 @@ constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; } +namespace v2ray { +constexpr char v2rayKeyPath[] = "/opt/amnezia/v2ray/v2ray.key"; +constexpr char defaultLocalPort[] = "1080"; +constexpr char defaultServerPort[] = "10086"; +} + namespace sftp { constexpr char defaultUserName[] = "sftp_user"; @@ -148,6 +154,7 @@ enum Proto { WireGuard, Ikev2, L2tp, + V2Ray, // non-vpn TorWebSite, diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 82ae08b8..fdf7a14f 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -9,7 +9,7 @@ #include ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration, QObject *parent): - OpenVpnProtocol(configuration, parent) + OpenVpnProtocol(configuration, parent) { readShadowSocksConfiguration(configuration); } @@ -32,7 +32,6 @@ ErrorCode ShadowSocksVpnProtocol::start() return lastError(); } - #ifndef Q_OS_IOS if (Utils::processIsRunning(Utils::executable("ss-local", false))) { Utils::killProcessByName(Utils::executable("ss-local", false)); @@ -48,30 +47,28 @@ ErrorCode ShadowSocksVpnProtocol::start() #ifdef Q_OS_LINUX QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName(); #else - QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName() - << "--no-delay"; + QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName() << "--no-delay"; #endif - qDebug().noquote() << "ShadowSocksVpnProtocol::start()" - << shadowSocksExecPath() << args.join(" "); + qDebug().noquote() << "ShadowSocksVpnProtocol::start()" << shadowSocksExecPath() << args.join(" "); m_ssProcess.setProcessChannelMode(QProcess::MergedChannels); m_ssProcess.setProgram(shadowSocksExecPath()); m_ssProcess.setArguments(args); - connect(&m_ssProcess, &QProcess::readyReadStandardOutput, this, [this](){ + 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){ + connect(&m_ssProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { qDebug().noquote() << "ShadowSocksVpnProtocol finished, exitCode, exiStatus" << exitCode << exitStatus; setConnectionState(VpnProtocol::Disconnected); - if (exitStatus != QProcess::NormalExit){ + if (exitStatus != QProcess::NormalExit) { emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed); stop(); } - if (exitCode !=0 ){ + if (exitCode !=0 ) { emit protocolError(amnezia::ErrorCode::InternalError); stop(); } @@ -84,8 +81,9 @@ ErrorCode ShadowSocksVpnProtocol::start() setConnectionState(VpnConnectionState::Connecting); return OpenVpnProtocol::start(); + } else { + return ErrorCode::ShadowSocksExecutableMissing; } - else return ErrorCode::ShadowSocksExecutableMissing; #else return ErrorCode::NotImplementedError; #endif @@ -116,17 +114,5 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath() void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration) { - QJsonObject shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject(); - bool isLocalPortConvertOk = false; - bool isServerPortConvertOk = false; - int localPort = shadowSocksConfig.value("local_port").toString().toInt(&isLocalPortConvertOk); - int serverPort = shadowSocksConfig.value("server_port").toString().toInt(&isServerPortConvertOk); - if (!isLocalPortConvertOk) { - qDebug() << "Error when converting local_port field in ShadowSocks config"; - } else if (!isServerPortConvertOk) { - qDebug() << "Error when converting server_port field in ShadowSocks config"; - } - shadowSocksConfig["local_port"] = localPort; - shadowSocksConfig["server_port"] = serverPort; - m_shadowSocksConfig = shadowSocksConfig; + m_shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject(); } diff --git a/client/protocols/shadowsocksvpnprotocol.h b/client/protocols/shadowsocksvpnprotocol.h index e5851255..a73ffaa4 100644 --- a/client/protocols/shadowsocksvpnprotocol.h +++ b/client/protocols/shadowsocksvpnprotocol.h @@ -1,9 +1,9 @@ #ifndef SHADOWSOCKSVPNPROTOCOL_H #define SHADOWSOCKSVPNPROTOCOL_H -#include "openvpnprotocol.h" #include "QProcess" -#include "containers/containers_defs.h" + +#include "openvpnprotocol.h" class ShadowSocksVpnProtocol : public OpenVpnProtocol { @@ -14,20 +14,18 @@ public: ErrorCode start() override; void stop() override; -protected: - void readShadowSocksConfiguration(const QJsonObject &configuration); - protected: QJsonObject m_shadowSocksConfig; -private: - static QString shadowSocksExecPath(); + void readShadowSocksConfiguration(const QJsonObject &configuration); private: #ifndef Q_OS_IOS QProcess m_ssProcess; #endif QTemporaryFile m_shadowSocksCfgFile; + + static QString shadowSocksExecPath(); }; #endif // SHADOWSOCKSVPNPROTOCOL_H diff --git a/client/protocols/v2rayprotocol.cpp b/client/protocols/v2rayprotocol.cpp new file mode 100644 index 00000000..9ed0ef8c --- /dev/null +++ b/client/protocols/v2rayprotocol.cpp @@ -0,0 +1,108 @@ +#include "v2rayprotocol.h" + +#include +#include +#include + +#include "utilities.h" + +V2RayProtocol::V2RayProtocol(const QJsonObject &configuration, QObject *parent) : OpenVpnProtocol(configuration, parent) +{ + writeV2RayConfiguration(configuration); +} + +V2RayProtocol::~V2RayProtocol() +{ + qDebug() << "V2RayProtocol::~V2RayProtocol"; + V2RayProtocol::stop(); + QThread::msleep(200); +} + +ErrorCode V2RayProtocol::start() +{ +#ifndef Q_OS_IOS + if (!QFileInfo::exists(v2RayExecPath())) { + setLastError(ErrorCode::V2RayExecutableMissing); + return lastError(); + } + + if (Utils::processIsRunning(Utils::executable("v2ray", false))) { + Utils::killProcessByName(Utils::executable("v2ray", false)); + } + + QStringList args = QStringList() << "-c" << m_v2RayConfigFile.fileName(); + + qDebug().noquote() << "V2RayProtocol::start()" << v2RayExecPath() << args.join(" "); + + m_v2RayProcess.setProcessChannelMode(QProcess::MergedChannels); + + m_v2RayProcess.setProgram(v2RayExecPath()); + m_v2RayProcess.setArguments(args); + + connect(&m_v2RayProcess, &QProcess::readyReadStandardOutput, this, [this]() { + qDebug().noquote() << "V2Ray:" << m_v2RayProcess.readAllStandardOutput(); + }); + + connect(&m_v2RayProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug().noquote() << "V2RayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus; + setConnectionState(VpnProtocol::Disconnected); + if (exitStatus != QProcess::NormalExit) { + emit protocolError(amnezia::ErrorCode::V2RayExecutableCrashed); + stop(); + } + if (exitCode != 0 ) { + emit protocolError(amnezia::ErrorCode::InternalError); + stop(); + } + }); + + m_v2RayProcess.start(); + m_v2RayProcess.waitForStarted(); + + if (m_v2RayProcess.state() == QProcess::ProcessState::Running) { + setConnectionState(VpnConnectionState::Connecting); + + return OpenVpnProtocol::start(); + } else { + return ErrorCode::V2RayExecutableMissing; + } +#else + return ErrorCode::NotImplementedError; +#endif + return ErrorCode::NoError; +} + +void V2RayProtocol::stop() +{ + OpenVpnProtocol::stop(); + + qDebug() << "V2RayProtocol::stop()"; +#ifndef Q_OS_IOS + m_v2RayProcess.terminate(); +#endif + +#ifdef Q_OS_WIN + Utils::signalCtrl(m_v2RayProcess.processId(), CTRL_C_EVENT); +#endif +} + +void V2RayProtocol::writeV2RayConfiguration(const QJsonObject &configuration) +{ + m_v2RayConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::V2Ray)).toObject(); + +#ifdef QT_DEBUG + m_v2RayConfigFile.setAutoRemove(false); +#endif + m_v2RayConfigFile.open(); + m_v2RayConfigFile.write(QJsonDocument(m_v2RayConfig).toJson()); + m_v2RayConfigFile.close(); +} + +const QString V2RayProtocol::v2RayExecPath() const +{ +#ifdef Q_OS_WIN + return Utils::executable(QString("v2ray/v2ray"), true); +#else + return Utils::executable(QString("v2ray"), true); +#endif +} diff --git a/client/protocols/v2rayprotocol.h b/client/protocols/v2rayprotocol.h new file mode 100644 index 00000000..9dcc75b2 --- /dev/null +++ b/client/protocols/v2rayprotocol.h @@ -0,0 +1,30 @@ +#ifndef V2RAYPROTOCOL_H +#define V2RAYPROTOCOL_H + +#include "QProcess" +#include "QTemporaryFile" + +#include "openvpnprotocol.h" + +class V2RayProtocol : public OpenVpnProtocol +{ +public: + V2RayProtocol(const QJsonObject& configuration, QObject* parent = nullptr); + virtual ~V2RayProtocol() override; + + ErrorCode start() override; + void stop() override; + +private: + QJsonObject m_v2RayConfig; + QTemporaryFile m_v2RayConfigFile; +#ifndef Q_OS_IOS + QProcess m_v2RayProcess; +#endif + + void writeV2RayConfiguration(const QJsonObject &configuration); + + const QString v2RayExecPath() const; +}; + +#endif // V2RAYPROTOCOL_H diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index a8f392e9..1312178c 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -9,6 +9,7 @@ #include "shadowsocksvpnprotocol.h" #include "openvpnovercloakprotocol.h" #include "wireguardprotocol.h" +#include "v2rayprotocol.h" #endif #ifdef Q_OS_WINDOWS @@ -114,6 +115,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject& case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration); case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration); case DockerContainer::WireGuard: return new WireguardProtocol(configuration); + case DockerContainer::V2Ray: return new V2RayProtocol(configuration); #endif default: return nullptr; } diff --git a/client/resources.qrc b/client/resources.qrc index 3aa23c64..09589281 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -166,5 +166,13 @@ ui/qml/Pages/PageAdvancedServerSettings.qml ui/qml/Controls/PopupWarning.qml ui/qml/Controls/PopupWithTextField.qml + ui/qml/Pages/Protocols/PageProtoV2Ray.qml + server_scripts/openvpn_v2ray_vmess/configure_container.sh + server_scripts/openvpn_v2ray_vmess/Dockerfile + server_scripts/openvpn_v2ray_vmess/run_container.sh + server_scripts/openvpn_v2ray_vmess/start.sh + server_scripts/openvpn_v2ray_vmess/template.ovpn + server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json + server_scripts/openvpn_shadowsocks/template_ss_client.json diff --git a/client/server_scripts/build_container.sh b/client/server_scripts/build_container.sh index 2cda240c..5a8b5457 100644 --- a/client/server_scripts/build_container.sh +++ b/client/server_scripts/build_container.sh @@ -1 +1 @@ -sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m) +sudo docker build --network host -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m) diff --git a/client/server_scripts/openvpn_shadowsocks/template_ss_client.json b/client/server_scripts/openvpn_shadowsocks/template_ss_client.json new file mode 100644 index 00000000..3119af8b --- /dev/null +++ b/client/server_scripts/openvpn_shadowsocks/template_ss_client.json @@ -0,0 +1,8 @@ +{ + "local_port": $SHADOWSOCKS_LOCAL_PORT, + "method": "$SHADOWSOCKS_CIPHER", + "password": "$SHADOWSOCKS_PASSWORD", + "server": "$REMOTE_HOST", + "server_port": $SHADOWSOCKS_SERVER_PORT, + "timeout": 60 +} diff --git a/client/server_scripts/openvpn_v2ray_vmess/Dockerfile b/client/server_scripts/openvpn_v2ray_vmess/Dockerfile new file mode 100644 index 00000000..32200ba8 --- /dev/null +++ b/client/server_scripts/openvpn_v2ray_vmess/Dockerfile @@ -0,0 +1,51 @@ +FROM alpine:3.15 + +LABEL maintainer="AmneziaVPN" + +#Install required packages +RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools +RUN apk --update upgrade --no-cache + +ENV EASYRSA_BATCH 1 +ENV PATH="/usr/share/easy-rsa:${PATH}" + +RUN mkdir -p /opt/amnezia +RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh +RUN chmod a+x /opt/amnezia/start.sh + +RUN apk add --no-cache v2ray + +# Tune network +RUN echo -e " \n\ + fs.file-max = 51200 \n\ + \n\ + net.core.rmem_max = 67108864 \n\ + net.core.wmem_max = 67108864 \n\ + net.core.netdev_max_backlog = 250000 \n\ + net.core.somaxconn = 4096 \n\ + \n\ + net.ipv4.tcp_syncookies = 1 \n\ + net.ipv4.tcp_tw_reuse = 1 \n\ + net.ipv4.tcp_tw_recycle = 0 \n\ + net.ipv4.tcp_fin_timeout = 30 \n\ + net.ipv4.tcp_keepalive_time = 1200 \n\ + net.ipv4.ip_local_port_range = 10000 65000 \n\ + net.ipv4.tcp_max_syn_backlog = 8192 \n\ + net.ipv4.tcp_max_tw_buckets = 5000 \n\ + net.ipv4.tcp_fastopen = 3 \n\ + net.ipv4.tcp_mem = 25600 51200 102400 \n\ + net.ipv4.tcp_rmem = 4096 87380 67108864 \n\ + net.ipv4.tcp_wmem = 4096 65536 67108864 \n\ + net.ipv4.tcp_mtu_probing = 1 \n\ + net.ipv4.tcp_congestion_control = hybla \n\ + # for low-latency network, use cubic instead \n\ + # net.ipv4.tcp_congestion_control = cubic \n\ + " | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \ + mkdir -p /etc/security && \ + echo -e " \n\ + * soft nofile 51200 \n\ + * hard nofile 51200 \n\ + " | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf + +ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ] +CMD [ "" ] diff --git a/client/server_scripts/openvpn_v2ray_vmess/configure_container.sh b/client/server_scripts/openvpn_v2ray_vmess/configure_container.sh new file mode 100644 index 00000000..31be0420 --- /dev/null +++ b/client/server_scripts/openvpn_v2ray_vmess/configure_container.sh @@ -0,0 +1,70 @@ +cat > /opt/amnezia/openvpn/server.conf < /opt/amnezia/v2ray/v2ray.key + +cat > /opt/amnezia/v2ray/v2ray-server.json < +$OPENVPN_CA_CERT + + +$OPENVPN_CLIENT_CERT + + +$OPENVPN_PRIV_KEY + + +$OPENVPN_TA_KEY + diff --git a/client/server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json b/client/server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json new file mode 100644 index 00000000..a0295336 --- /dev/null +++ b/client/server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json @@ -0,0 +1,31 @@ +{ + "inbounds": [ + { + "listen": "127.0.0.1", + "port": $V2RAY_SOCKS_LOCAL_PORT, + "protocol": "socks", + "settings": { + "auth": "noauth", + "udp": true + } + } + ], + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "vnext": [ + { + "address": "$REMOTE_HOST", + "port": $V2RAY_VMESS_PORT, + "users": [ + { + "id": "$V2RAY_VMESS_CLIENT_UUID" + } + ] + } + ] + } + } + ] +} diff --git a/client/ui/pages_logic/ServerConfiguringProgressLogic.h b/client/ui/pages_logic/ServerConfiguringProgressLogic.h index 6ce2d4e7..b144404e 100644 --- a/client/ui/pages_logic/ServerConfiguringProgressLogic.h +++ b/client/ui/pages_logic/ServerConfiguringProgressLogic.h @@ -55,6 +55,7 @@ public: friend class ShadowSocksLogic; friend class CloakLogic; friend class UiLogic; + friend class V2RayLogic; void onUpdatePage() override; ErrorCode doInstallAction(const std::function &action); diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index fe3a2ed6..83cec16f 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -166,7 +166,7 @@ void OpenVpnLogic::onPushButtonSaveClicked() auto installAction = [this, containerConfig, &newContainerConfig]() { ServerController serverController(m_settings); return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), - uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig); + uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig); }; ErrorCode e = uiLogic()->pageLogic()->doInstallAction(installAction, pageFunc, progressBarFunc, diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp index b1d75b8b..8ecc057d 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp @@ -50,6 +50,7 @@ QJsonObject ShadowSocksLogic::getProtocolConfigFromPage(QJsonObject oldConfig) void ShadowSocksLogic::onPushButtonSaveClicked() { QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, Proto::ShadowSocks); + protocolConfig = getProtocolConfigFromPage(protocolConfig); QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer); QJsonObject newContainerConfig = containerConfig; @@ -108,7 +109,7 @@ void ShadowSocksLogic::onPushButtonSaveClicked() auto installAction = [this, containerConfig, &newContainerConfig]() { ServerController serverController(m_settings); return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), - uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig); + uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig); }; ErrorCode e = uiLogic()->pageLogic()->doInstallAction(installAction, pageFunc, progressBarFunc, saveButtonFunc, waitInfoFunc, diff --git a/client/ui/pages_logic/protocols/V2RayLogic.cpp b/client/ui/pages_logic/protocols/V2RayLogic.cpp new file mode 100644 index 00000000..c950bcc7 --- /dev/null +++ b/client/ui/pages_logic/protocols/V2RayLogic.cpp @@ -0,0 +1,128 @@ +#include "V2RayLogic.h" + +#include + +#include "core/servercontroller.h" +#include "ui/pages_logic/ServerConfiguringProgressLogic.h" +#include "ui/uilogic.h" + +using namespace amnezia; +using namespace PageEnumNS; + +V2RayLogic::V2RayLogic(UiLogic *logic, QObject *parent): + PageProtocolLogicBase(logic, parent), + m_lineEditServerPortText{}, + m_pushButtonSaveVisible{false}, + m_progressBarResetVisible{false}, + m_lineEditServerPortEnabled{false}, + m_labelInfoVisible{true}, + m_labelInfoText{}, + m_progressBarResetValue{0}, + m_progressBarResetMaximium{100}, + m_lineEditLocalPortEnabled{false}, + m_lineEditLocalPortText{} +{ + +} + +void V2RayLogic::updateProtocolPage(const QJsonObject &v2RayConfig, DockerContainer container, bool haveAuthData) +{ + set_pageEnabled(haveAuthData); + set_pushButtonSaveVisible(haveAuthData); + set_progressBarResetVisible(haveAuthData); + + set_lineEditServerPortText(v2RayConfig.value(config_key::port).toString(protocols::v2ray::defaultServerPort)); + set_lineEditServerPortEnabled(container == DockerContainer::V2Ray); + + set_lineEditLocalPortText(v2RayConfig.value(config_key::local_port).toString(protocols::v2ray::defaultLocalPort)); + set_lineEditLocalPortEnabled(container == DockerContainer::V2Ray); + +} + +QJsonObject V2RayLogic::getProtocolConfigFromPage(QJsonObject oldConfig) +{ + oldConfig.insert(config_key::port, lineEditServerPortText()); + oldConfig.insert(config_key::local_port, lineEditLocalPortText()); + return oldConfig; +} + +void V2RayLogic::onPushButtonSaveClicked() +{ + QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, Proto::V2Ray); + protocolConfig = getProtocolConfigFromPage(protocolConfig); + + QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer); + QJsonObject newContainerConfig = containerConfig; + newContainerConfig.insert(ProtocolProps::protoToString(Proto::V2Ray), protocolConfig); + ServerConfiguringProgressLogic::PageFunc pageFunc; + pageFunc.setEnabledFunc = [this] (bool enabled) -> void { + set_pageEnabled(enabled); + }; + ServerConfiguringProgressLogic::ButtonFunc saveButtonFunc; + saveButtonFunc.setVisibleFunc = [this] (bool visible) -> void { + set_pushButtonSaveVisible(visible); + }; + ServerConfiguringProgressLogic::LabelFunc waitInfoFunc; + waitInfoFunc.setVisibleFunc = [this] (bool visible) -> void { + set_labelInfoVisible(visible); + }; + waitInfoFunc.setTextFunc = [this] (const QString& text) -> void { + set_labelInfoText(text); + }; + ServerConfiguringProgressLogic::ProgressFunc progressBarFunc; + progressBarFunc.setVisibleFunc = [this] (bool visible) -> void { + set_progressBarResetVisible(visible); + }; + progressBarFunc.setValueFunc = [this] (int value) -> void { + set_progressBarResetValue(value); + }; + progressBarFunc.getValueFunc = [this] (void) -> int { + return progressBarResetValue(); + }; + progressBarFunc.getMaximiumFunc = [this] (void) -> int { + return progressBarResetMaximium(); + }; + progressBarFunc.setTextVisibleFunc = [this] (bool visible) -> void { + set_progressBarTextVisible(visible); + }; + progressBarFunc.setTextFunc = [this] (const QString& text) -> void { + set_progressBarText(text); + }; + + ServerConfiguringProgressLogic::LabelFunc busyInfoFuncy; + busyInfoFuncy.setTextFunc = [this] (const QString& text) -> void { + set_labelServerBusyText(text); + }; + busyInfoFuncy.setVisibleFunc = [this] (bool visible) -> void { + set_labelServerBusyVisible(visible); + }; + + ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc; + cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void { + set_pushButtonCancelVisible(visible); + }; + + progressBarFunc.setTextVisibleFunc(true); + progressBarFunc.setTextFunc(QString("Configuring...")); + + auto installAction = [this, containerConfig, &newContainerConfig]() { + ServerController serverController(m_settings); + return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), + uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig); + }; + + ErrorCode e = uiLogic()->pageLogic()->doInstallAction(installAction, pageFunc, progressBarFunc, + saveButtonFunc, waitInfoFunc, + busyInfoFuncy, cancelButtonFunc); + + if (!e) { + m_settings->setContainerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, newContainerConfig); + m_settings->clearLastConnectionConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer); + } + qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->m_selectedServerIndex << uiLogic()->m_selectedDockerContainer; +} + +void V2RayLogic::onPushButtonCancelClicked() +{ + emit uiLogic()->pageLogic()->cancelDoInstallAction(true); +} diff --git a/client/ui/pages_logic/protocols/V2RayLogic.h b/client/ui/pages_logic/protocols/V2RayLogic.h new file mode 100644 index 00000000..4f30c667 --- /dev/null +++ b/client/ui/pages_logic/protocols/V2RayLogic.h @@ -0,0 +1,50 @@ +#ifndef V2RAYLOGIC_H +#define V2RAYLOGIC_H + +#include "PageProtocolLogicBase.h" + +class UiLogic; + +class V2RayLogic : public PageProtocolLogicBase +{ + Q_OBJECT + + AUTO_PROPERTY(bool, lineEditServerPortEnabled) + AUTO_PROPERTY(QString, lineEditServerPortText) + + AUTO_PROPERTY(bool, lineEditLocalPortEnabled) + AUTO_PROPERTY(QString, lineEditLocalPortText) + + AUTO_PROPERTY(bool, labelInfoVisible) + AUTO_PROPERTY(QString, labelInfoText) + + AUTO_PROPERTY(int, progressBarResetValue) + AUTO_PROPERTY(int, progressBarResetMaximium) + AUTO_PROPERTY(bool, progressBarResetVisible) + AUTO_PROPERTY(bool, progressBarTextVisible) + AUTO_PROPERTY(QString, progressBarText) + + AUTO_PROPERTY(bool, labelServerBusyVisible) + AUTO_PROPERTY(QString, labelServerBusyText) + + AUTO_PROPERTY(bool, pushButtonSaveVisible) + AUTO_PROPERTY(bool, pushButtonCancelVisible) + +public: + Q_INVOKABLE void onPushButtonSaveClicked(); + Q_INVOKABLE void onPushButtonCancelClicked(); + +public: + explicit V2RayLogic(UiLogic *uiLogic, QObject *parent = nullptr); + ~V2RayLogic() = default; + + void updateProtocolPage(const QJsonObject &v2rayConfig, DockerContainer container, bool haveAuthData) override; + QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; + +private: + UiLogic *m_uiLogic; + +}; + + +#endif // V2RAYLOGIC_H diff --git a/client/ui/qml/Pages/Protocols/PageProtoV2Ray.qml b/client/ui/qml/Pages/Protocols/PageProtoV2Ray.qml new file mode 100644 index 00000000..7f06c9bd --- /dev/null +++ b/client/ui/qml/Pages/Protocols/PageProtoV2Ray.qml @@ -0,0 +1,158 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageProtocolBase { + id: root + protocol: ProtocolEnum.V2Ray + logic: UiLogic.protocolLogic(protocol) + + BackButton { + id: back + enabled: !logic.pushButtonCancelVisible + } + + Caption { + id: caption + text: qsTr("V2Ray Settings") + } + + ColumnLayout { + id: content + enabled: logic.pageEnabled + anchors.top: caption.bottom + anchors.left: root.left + anchors.right: root.right + anchors.bottom: pb_save.top + anchors.margins: 20 + anchors.topMargin: 10 + + RowLayout { + Layout.fillWidth: true + + LabelType { + Layout.preferredWidth: 0.3 * root.width - 10 + height: 31 + text: qsTr("Port") + } + + TextFieldType { + Layout.fillWidth: true + height: 31 + text: logic.lineEditServerPortText + onEditingFinished: { + logic.lineEditServerPortText = text + } + enabled: logic.lineEditServerPortEnabled + } + } + + RowLayout { + Layout.fillWidth: true + + LabelType { + Layout.preferredWidth: 0.3 * root.width - 10 + height: 31 + text: qsTr("Local port") + } + + TextFieldType { + Layout.fillWidth: true + height: 31 + text: logic.lineEditLocalPortText + onEditingFinished: { + logic.lineEditLocalPortText = text + } + enabled: logic.lineEditLocalPortEnabled + } + } + + Item { + Layout.fillHeight: true + } + + LabelType { + horizontalAlignment: Text.AlignHCenter + Layout.maximumWidth: parent.width + Layout.fillWidth: true + visible: logic.labelServerBusyVisible + text: logic.labelServerBusyText + } + + LabelType { + horizontalAlignment: Text.AlignHCenter + Layout.maximumWidth: parent.width + Layout.fillWidth: true + visible: logic.labelInfoVisible + text: logic.labelInfoText + } + } + + ProgressBar { + id: progressBar_reset + anchors.fill: pb_save + from: 0 + to: logic.progressBarResetMaximium + value: logic.progressBarResetValue + visible: logic.progressBarResetVisible + background: Rectangle { + implicitWidth: parent.width + implicitHeight: parent.height + color: "#100A44" + radius: 4 + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: parent.height + Rectangle { + width: progressBar_reset.visualPosition * parent.width + height: parent.height + radius: 4 + color: Qt.rgba(255, 255, 255, 0.15); + } + } + LabelType { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: logic.progressBarText + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + color: "#D4D4D4" + visible: logic.progressBarTextVisible + } + } + + BlueButtonType { + id: pb_save + enabled: logic.pageEnabled + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: root.bottom + anchors.bottomMargin: 20 + width: root.width - 60 + height: 40 + text: qsTr("Save and restart VPN") + visible: logic.pushButtonSaveVisible + onClicked: { + logic.onPushButtonSaveClicked() + } + } + + BlueButtonType { + anchors.fill: pb_save + text: qsTr("Cancel") + visible: logic.pushButtonCancelVisible + enabled: logic.pushButtonCancelVisible + onClicked: { + logic.onPushButtonCancelClicked() + } + } +} diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index cc66a47d..97e7be05 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -72,6 +72,7 @@ #include "pages_logic/protocols/ShadowSocksLogic.h" #include "pages_logic/protocols/OtherProtocolsLogic.h" #include "pages_logic/protocols/WireGuardLogic.h" +#include "pages_logic/protocols/V2RayLogic.h" using namespace amnezia; using namespace PageEnumNS; @@ -93,6 +94,7 @@ UiLogic::UiLogic(std::shared_ptr settings, std::shared_ptr