Compare commits

...

11 Commits

Author SHA1 Message Date
vladimir.kuznetsov e9468a4c2f fixed display of cancel button on install/uninstall pages 2024-03-31 12:40:42 +05:00
pokamest db8d966fac Merge pull request #714 from amnezia-vpn/bugfix/wg-and-xray-remove-button 2024-03-29 09:40:13 +00:00
pokamest 6b69bc9618 Tiny fixes 2024-03-28 17:13:48 +00:00
pokamest 0089b0b799 Error code 206 description 2024-03-28 15:01:17 +00:00
vladimir.kuznetsov e4841e809b fixed remove button for wireguard and xray settings page 2024-03-28 15:33:23 +05:00
Mykola Baibuz ba4237f1dd Xray with Reality protocol (#494)
* Xray with Reality for desktops
2024-03-27 11:02:34 +00:00
pokamest f6acec53c0 Merge pull request #712 from amnezia-vpn/bugfix/page-home-recursive-rearrange
fixed recursive rearrange on PageHome
2024-03-27 10:59:01 +00:00
Shehab Ahmed 5f631eaa76 Refactoring/change application text (#687)
Changing some texts
2024-03-26 18:05:04 +00:00
albexk 7730dd510c Add error handling of enabled "always-on" during VPN connection (#698)
* Always add awg-go version to the log
* Display an error message always when no vpn permission is granted
2024-03-25 23:09:50 +00:00
pokamest 30bd264f17 Merge pull request #711 from amnezia-vpn/bugfix/anchors-page-home-warning
fixed anchors warning on PageHome
2024-03-25 18:38:20 +00:00
vladimir.kuznetsov 5206665fa0 fixed recursive rearrange on PageHome 2024-03-25 22:30:44 +05:00
99 changed files with 3927 additions and 2131 deletions
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.4.2.1
project(${PROJECT} VERSION 4.5.0.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 49)
set(APP_ANDROID_VERSION_CODE 50)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
+6 -2
View File
@@ -63,10 +63,10 @@ qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
set(CMAKE_AUTORCC ON)
set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru_RU.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar_EG.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_my_MM.ts
)
@@ -129,6 +129,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
${CMAKE_CURRENT_BINARY_DIR}/version.h
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h
)
# Mozilla headres
@@ -164,6 +165,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp
)
# Mozilla sources
@@ -293,6 +295,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/xrayprotocol.h
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
)
@@ -304,6 +307,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/xrayprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
)
endif()
+3
View File
@@ -343,6 +343,9 @@ void AmneziaApplication::initModels()
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
m_xrayConfigModel.reset(new XrayConfigModel(this));
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
+2
View File
@@ -36,6 +36,7 @@
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols/xrayConfigModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
@@ -102,6 +103,7 @@ private:
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
+7
View File
@@ -2,4 +2,11 @@
<resources>
<string name="connecting">Подключение</string>
<string name="disconnecting">Отключение</string>
<string name="cancel">Отмена</string>
<string name="ok">ОК</string>
<string name="vpnGranted">VPN-подключение разрешено</string>
<string name="vpnDenied">VPN-подключение запрещено</string>
<string name="vpnSetupFailed">Ошибка настройки VPN</string>
<string name="vpnSetupFailedMessage">Чтобы подключиться к AmneziaVPN необходимо:\n\n- Разрешить приложению подключаться к сети VPN\n- Отключить функцию \"Постоянная VPN\" для всех остальных VPN-приложений в системных настройках VPN</string>
<string name="openVpnSettings">Открыть настройки VPN</string>
</resources>
+7
View File
@@ -2,4 +2,11 @@
<resources>
<string name="connecting">Connecting</string>
<string name="disconnecting">Disconnecting</string>
<string name="cancel">Cancel</string>
<string name="ok">OK</string>
<string name="vpnGranted">VPN permission granted</string>
<string name="vpnDenied">VPN permission denied</string>
<string name="vpnSetupFailed">VPN setup error</string>
<string name="vpnSetupFailedMessage">To connect to AmneziaVPN, please do the following:\n\n- Allow the app to set up a VPN connection\n- Disable Always-on VPN for any other VPN app in the VPN system settings</string>
<string name="openVpnSettings">Open VPN settings</string>
</resources>
@@ -1,5 +1,6 @@
package org.amnezia.vpn
import android.app.AlertDialog
import android.content.ComponentName
import android.content.Intent
import android.content.Intent.EXTRA_MIME_TYPES
@@ -14,6 +15,7 @@ import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.provider.Settings
import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap
import android.widget.Toast
@@ -216,13 +218,13 @@ class AmneziaActivity : QtActivity() {
when (resultCode) {
RESULT_OK -> {
Log.d(TAG, "Vpn permission granted")
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
Toast.makeText(this, resources.getText(R.string.vpnGranted), Toast.LENGTH_LONG).show()
checkVpnPermissionCallbacks?.run { onSuccess() }
}
else -> {
Log.w(TAG, "Vpn permission denied, resultCode: $resultCode")
Toast.makeText(this, "Vpn permission denied", Toast.LENGTH_LONG).show()
showOnVpnPermissionRejectDialog()
checkVpnPermissionCallbacks?.run { onFail() }
}
}
@@ -280,6 +282,17 @@ class AmneziaActivity : QtActivity() {
onSuccess()
}
private fun showOnVpnPermissionRejectDialog() {
AlertDialog.Builder(this)
.setTitle(R.string.vpnSetupFailed)
.setMessage(R.string.vpnSetupFailedMessage)
.setNegativeButton(R.string.ok) { _, _ -> }
.setPositiveButton(R.string.openVpnSettings) { _, _ ->
startActivity(Intent(Settings.ACTION_VPN_SETTINGS))
}
.show()
}
@MainThread
private fun startVpn(vpnConfig: String) {
if (isServiceConnected) {
@@ -215,11 +215,9 @@ class AmneziaVpnService : VpnService() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val isAlwaysOnCompat =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) isAlwaysOn
else intent?.component?.packageName != packageName
val isAlwaysOn = intent != null && intent.action == SERVICE_INTERFACE
if (isAlwaysOnCompat) {
if (isAlwaysOn) {
Log.d(TAG, "Start service via Always-on")
connect()
} else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) {
@@ -1,12 +1,16 @@
package org.amnezia.vpn
import android.app.AlertDialog
import android.app.KeyguardManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.net.VpnService
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResult
@@ -52,19 +56,43 @@ class VpnRequestActivity : ComponentActivity() {
}
private fun checkRequestResult(result: ActivityResult) {
when (result.resultCode) {
RESULT_OK -> onPermissionGranted()
else -> Toast.makeText(this, "Vpn permission denied", Toast.LENGTH_LONG).show()
when (val resultCode = result.resultCode) {
RESULT_OK -> {
onPermissionGranted()
finish()
}
else -> {
Log.w(TAG, "Vpn permission denied, resultCode: $resultCode")
showOnVpnPermissionRejectDialog()
}
}
finish()
}
private fun onPermissionGranted() {
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
Toast.makeText(this, resources.getString(R.string.vpnGranted), Toast.LENGTH_LONG).show()
Intent(applicationContext, AmneziaVpnService::class.java).apply {
putExtra(AFTER_PERMISSION_CHECK, true)
}.also {
ContextCompat.startForegroundService(this, it)
}
}
private fun showOnVpnPermissionRejectDialog() {
AlertDialog.Builder(this, getDialogTheme())
.setTitle(R.string.vpnSetupFailed)
.setMessage(R.string.vpnSetupFailedMessage)
.setNegativeButton(R.string.ok) { _, _ -> }
.setPositiveButton(R.string.openVpnSettings) { _, _ ->
startActivity(Intent(Settings.ACTION_VPN_SETTINGS))
}
.setOnDismissListener { finish() }
.show()
}
private fun getDialogTheme(): Int =
if (resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES)
android.R.style.Theme_DeviceDefault_Dialog_Alert
else
android.R.style.Theme_DeviceDefault_Light_Dialog_Alert
}
@@ -157,7 +157,7 @@ open class Wireguard : Protocol() {
if (tunFd == null) {
throw VpnStartException("Create VPN interface: permission not granted or revoked")
}
Log.v(TAG, "Wg-go backend ${GoBackend.awgVersion()}")
Log.i(TAG, "awg-go backend ${GoBackend.awgVersion()}")
tunnelHandle = GoBackend.awgTurnOn(ifName, tunFd.detachFd(), config.toWgUserspaceString())
}
-2
View File
@@ -42,9 +42,7 @@ set(SOURCES ${SOURCES}
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-go.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-quick.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libck-ovpn-plugin.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.so
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so
@@ -48,6 +48,5 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
serverController.genVarsForScript(credentials, container, containerConfig));
// qDebug().noquote() << textCfg;
return textCfg;
}
+8 -4
View File
@@ -6,14 +6,14 @@
#include "ssh_configurator.h"
#include "wireguard_configurator.h"
#include "awg_configurator.h"
#include "xray_configurator.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include "containers/containers_defs.h"
#include "settings.h"
#include "utilities.h"
#include "core/networkUtilities.h"
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
: ConfiguratorBase(settings, parent)
@@ -25,6 +25,7 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *pa
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, this));
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
awgConfigurator = std::shared_ptr<AwgConfigurator>(new AwgConfigurator(settings, this));
xrayConfigurator = std::shared_ptr<XrayConfigurator>(new XrayConfigurator(settings, this));
}
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
@@ -45,6 +46,9 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia
case Proto::Awg:
return awgConfigurator->genAwgConfig(credentials, container, containerConfig, clientId, errorCode);
case Proto::Xray:
return xrayConfigurator->genXrayConfig(credentials, container, containerConfig, clientId, errorCode);
case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
default: return "";
@@ -61,13 +65,13 @@ QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
dns.first = server.value(config_key::dns1).toString();
dns.second = server.value(config_key::dns2).toString();
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) {
dns.first = protocols::dns::amneziaDnsIp;
} else
dns.first = m_settings->primaryDns();
}
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
dns.second = m_settings->secondaryDns();
}
+2
View File
@@ -13,6 +13,7 @@ class WireguardConfigurator;
class Ikev2Configurator;
class SshConfigurator;
class AwgConfigurator;
class XrayConfigurator;
// Retrieve connection settings from server
class VpnConfigurator : public ConfiguratorBase
@@ -42,6 +43,7 @@ public:
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
std::shared_ptr<SshConfigurator> sshConfigurator;
std::shared_ptr<AwgConfigurator> awgConfigurator;
std::shared_ptr<XrayConfigurator> xrayConfigurator;
signals:
void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container,
@@ -0,0 +1,49 @@
#include "xray_configurator.h"
#include <QFile>
#include <QJsonObject>
#include <QJsonDocument>
#include "core/scripts_registry.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
QString XrayConfigurator::genXrayConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
QString config =
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
QString xrayPublicKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::xray::PublicKeyPath, &e);
xrayPublicKey.replace("\n", "");
QString xrayUuid = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::xray::uuidPath, &e);
xrayUuid.replace("\n", "");
QString xrayShortId = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::xray::shortidPath, &e);
xrayShortId.replace("\n", "");
if (e) {
if (errorCode) *errorCode = e;
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayUuid);
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
config.replace("$XRAY_SHORT_ID", xrayShortId);
return config;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef XRAY_CONFIGURATOR_H
#define XRAY_CONFIGURATOR_H
#include <QObject>
#include "configurator_base.h"
#include "core/defs.h"
class XrayConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
XrayConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genXrayConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
};
#endif // XRAY_CONFIGURATOR_H
+23 -2
View File
@@ -58,6 +58,8 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ };
case DockerContainer::Xray: return { Proto::Xray };
case DockerContainer::Dns: return { Proto::Dns };
case DockerContainer::Sftp: return { Proto::Sftp };
@@ -85,6 +87,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
{ DockerContainer::WireGuard, "WireGuard" },
{ DockerContainer::Awg, "AmneziaWG" },
{ DockerContainer::Xray, "XRay" },
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
@@ -111,6 +114,9 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
"but very resistant to blockages. "
"Recommended for regions with high levels of censorship.") },
{ DockerContainer::Xray,
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
"signal loss. It has native support on the latest versions of Android and iOS.") },
@@ -199,6 +205,17 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"* Minimum number of settings\n"
"* Not recognised by DPI analysis systems, resistant to blocking\n"
"* Works over UDP network protocol.") },
{ DockerContainer::Xray,
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
"thus presenting an authentic TLS certificate and data. \n"
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
"legitimate sites without the need for specific configurations. \n"
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
},
{ DockerContainer::Ipsec,
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
@@ -213,7 +230,11 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("DNS Service") },
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service") }
{ DockerContainer::Sftp,
QObject::tr("After installation, Amnezia will create a\n\n file storage on your server. "
"You will be able to access it using\n FileZilla or other SFTP clients, "
"as well as mount the disk on your device to access\n it directly from your device.\n\n"
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") }
};
}
@@ -231,6 +252,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
case DockerContainer::WireGuard: return Proto::WireGuard;
case DockerContainer::Awg: return Proto::Awg;
case DockerContainer::Xray: return Proto::Xray;
case DockerContainer::Ipsec: return Proto::Ikev2;
case DockerContainer::TorWebSite: return Proto::TorWebSite;
@@ -274,7 +296,6 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
#elif defined(Q_OS_LINUX)
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::Ipsec: return false;
default: return true;
}
+1
View File
@@ -22,6 +22,7 @@ namespace amnezia
Cloak,
ShadowSocks,
Ipsec,
Xray,
// non-vpn
TorWebSite,
+7 -7
View File
@@ -26,6 +26,7 @@
#include "logger.h"
#include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "core/networkUtilities.h"
#include "settings.h"
#include "utilities.h"
@@ -444,9 +445,6 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
stdOut += data + "\n";
return ErrorCode::NoError;
};
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
// };
e = runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
@@ -466,9 +464,6 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
stdOut += data + "\n";
return ErrorCode::NoError;
};
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
// };
ErrorCode e = runScript(credentials,
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
@@ -537,6 +532,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 &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
Vars vars;
@@ -594,6 +590,10 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$FAKE_WEB_SITE_ADDRESS",
cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
// Xray vars
vars.append({ { "$XRAY_SITE_NAME",
xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
// Wireguard vars
vars.append(
{ { "$WIREGUARD_SUBNET_IP",
@@ -652,7 +652,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER",
amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
QString serverIp = Utils::getIPAddress(credentials.hostName);
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
if (!serverIp.isEmpty()) {
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
} else {
+4
View File
@@ -59,6 +59,8 @@ namespace amnezia
CloakExecutableMissing = 602,
AmneziaServiceConnectionFailed = 603,
ExecutableMissing = 604,
XrayExecutableMissing = 605,
Tun2SockExecutableMissing = 606,
// VPN errors
OpenVpnAdaptersInUseError = 700,
@@ -70,6 +72,8 @@ namespace amnezia
OpenSslFailed = 800,
ShadowSocksExecutableCrashed = 801,
CloakExecutableCrashed = 802,
XrayExecutableCrashed = 803,
Tun2SockExecutableCrashed = 804,
// import and install errors
ImportInvalidConfigError = 900,
+1
View File
@@ -19,6 +19,7 @@ QString errorString(ErrorCode code) {
case(ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
case(ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
case(ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
// Libssh errors
case(SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break;
+439
View File
@@ -0,0 +1,439 @@
#include "networkUtilities.h"
#ifdef Q_OS_WIN
#include <windows.h>
#include <Ipexport.h>
#include <Ws2tcpip.h>
#include <ws2ipdef.h>
#include <stdint.h>
#include <Iphlpapi.h>
#include <Iptypes.h>
#include <WinSock2.h>
#include <winsock.h>
#endif
#ifdef Q_OS_LINUX
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/route.h>
#endif
#include <QHostAddress>
#include <QHostInfo>
QRegularExpression NetworkUtilities::ipAddressRegExp()
{
return QRegularExpression("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$");
}
QRegularExpression NetworkUtilities::ipAddressPortRegExp()
{
return QRegularExpression("^(?:(?: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}$");
}
QRegExp NetworkUtilities::ipAddressWithSubnetRegExp()
{
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,2}){0,1}");
}
QRegExp NetworkUtilities::ipNetwork24RegExp()
{
return QRegExp("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}"
"0$");
}
QRegExp NetworkUtilities::ipPortRegExp()
{
return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$");
}
QRegExp NetworkUtilities::domainRegExp()
{
return QRegExp("(((?!\\-))(xn\\-\\-)?[a-z0-9\\-_]{0,61}[a-z0-9]{1,1}\\.)*(xn\\-\\-)?([a-z0-9\\-]{1,61}|[a-z0-"
"9\\-]{1,30})\\.[a-z]{2,}");
}
QString NetworkUtilities::netMaskFromIpWithSubnet(const QString ip)
{
if (!ip.contains("/"))
return "255.255.255.255";
bool ok;
int prefix = ip.split("/").at(1).toInt(&ok);
if (!ok)
return "255.255.255.255";
unsigned long mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;
return QString("%1.%2.%3.%4").arg(mask >> 24).arg((mask >> 16) & 0xFF).arg((mask >> 8) & 0xFF).arg(mask & 0xFF);
}
QString NetworkUtilities::ipAddressFromIpWithSubnet(const QString ip)
{
if (ip.count(".") != 3)
return "";
return ip.split("/").first();
}
QStringList NetworkUtilities::summarizeRoutes(const QStringList &ips, const QString cidr)
{
// QMap<int, int>
// QHostAddress
// QMap<QString, QStringList> subnets; // <"a.b", <list subnets>>
// for (const QString &ip : ips) {
// if (ip.count(".") != 3) continue;
// const QStringList &parts = ip.split(".");
// subnets[parts.at(0) + "." + parts.at(1)].append(ip);
// }
return QStringList();
}
QString NetworkUtilities::getIPAddress(const QString &host)
{
if (ipAddressRegExp().match(host).hasMatch()) {
return host;
}
QList<QHostAddress> addresses = QHostInfo::fromName(host).addresses();
if (!addresses.isEmpty()) {
return addresses.first().toString();
}
qDebug() << "Unable to resolve address for " << host;
return "";
}
QString NetworkUtilities::getStringBetween(const QString &s, const QString &a, const QString &b)
{
int ap = s.indexOf(a), bp = s.indexOf(b, ap + a.length());
if (ap < 0 || bp < 0)
return QString();
ap += a.length();
if (bp - ap <= 0)
return QString();
return s.mid(ap, bp - ap).trimmed();
}
bool NetworkUtilities::checkIPv4Format(const QString &ip)
{
if (ip.isEmpty())
return false;
int count = ip.count(".");
if (count != 3)
return false;
QHostAddress addr(ip);
return (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol);
}
bool NetworkUtilities::checkIpSubnetFormat(const QString &ip)
{
if (!ip.contains("/"))
return checkIPv4Format(ip);
QStringList parts = ip.split("/");
if (parts.size() != 2)
return false;
bool ok;
int subnet = parts.at(1).toInt(&ok);
if (subnet >= 0 && subnet <= 32 && ok)
return checkIPv4Format(parts.at(0));
else
return false;
}
#ifdef Q_OS_WIN
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
const ULONG Flags,
const PVOID Reserved,
_Out_ PIP_ADAPTER_ADDRESSES& pAdapterAddresses) {
DWORD dwRetVal = 0;
int iter = 0;
constexpr int max_iter = 3;
ULONG AdapterAddressesLen = 15000;
do {
// xassert2(pAdapterAddresses == nullptr);
pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)malloc(AdapterAddressesLen);
if (pAdapterAddresses == nullptr) {
qDebug() << "can not malloc" << AdapterAddressesLen << "bytes";
return ERROR_OUTOFMEMORY;
}
dwRetVal = GetAdaptersAddresses(Family, Flags, NULL, pAdapterAddresses, &AdapterAddressesLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
free(pAdapterAddresses);
pAdapterAddresses = nullptr;
} else {
break;
}
iter++;
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (iter < max_iter));
if (dwRetVal != NO_ERROR) {
qDebug() << "Family: " << Family << ", Flags: " << Flags << " AdapterAddressesLen: " << AdapterAddressesLen <<
", dwRetVal:" << dwRetVal << ", iter: " << iter;
if (pAdapterAddresses) {
free(pAdapterAddresses);
pAdapterAddresses = nullptr;
}
}
return dwRetVal;
}
#endif
QString NetworkUtilities::getGatewayAndIface()
{
#ifdef Q_OS_WIN
constexpr int BUFF_LEN = 100;
char buff[BUFF_LEN] = {'\0'};
QString result;
PIP_ADAPTER_ADDRESSES pAdapterAddresses = nullptr;
DWORD dwRetVal =
GetAdaptersAddressesWrapper(AF_INET, GAA_FLAG_INCLUDE_GATEWAYS, NULL, pAdapterAddresses);
if (dwRetVal != NO_ERROR) {
qDebug() << "ipv4 stack detect GetAdaptersAddresses failed.";
return "";
}
PIP_ADAPTER_ADDRESSES pCurAddress = pAdapterAddresses;
while (pCurAddress) {
PIP_ADAPTER_GATEWAY_ADDRESS_LH gateway = pCurAddress->FirstGatewayAddress;
if (gateway) {
SOCKET_ADDRESS gateway_address = gateway->Address;
if (gateway->Address.lpSockaddr->sa_family == AF_INET) {
sockaddr_in* sa_in = (sockaddr_in*)gateway->Address.lpSockaddr;
QString gw = inet_ntop(AF_INET, &(sa_in->sin_addr), buff, BUFF_LEN);
qDebug() << "gateway IPV4:" << gw;
struct sockaddr_in addr;
if (inet_pton(AF_INET, buff, &addr.sin_addr) == 1) {
qDebug() << "this is true v4 !";
result = gw;
}
}
}
pCurAddress = pCurAddress->Next;
}
free(pAdapterAddresses);
return result;
#endif
#ifdef Q_OS_LINUX
constexpr int BUFFER_SIZE = 100;
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
int sock = -1, msgseq = 0;
struct nlmsghdr *nlh, *nlmsg;
struct rtmsg *route_entry;
// This struct contain route attributes (route type)
struct rtattr *route_attribute;
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
char *ptr = buffer;
struct timeval tv;
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
perror("socket failed");
return "";
}
memset(msgbuf, 0, sizeof(msgbuf));
memset(gateway_address, 0, sizeof(gateway_address));
memset(interface, 0, sizeof(interface));
memset(buffer, 0, sizeof(buffer));
/* point the header and the msg structure pointers into the buffer */
nlmsg = (struct nlmsghdr *)msgbuf;
/* Fill in the nlmsg header*/
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
/* 1 Sec Timeout to avoid stall */
tv.tv_sec = 1;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
/* send msg */
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
perror("send failed");
return "";
}
/* receive response */
do
{
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
if (received_bytes < 0) {
perror("Error in recv");
return "";
}
nlh = (struct nlmsghdr *) ptr;
/* Check if the header is valid */
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
(nlmsg->nlmsg_type == NLMSG_ERROR))
{
perror("Error in received packet");
return "";
}
/* If we received all data break */
if (nlh->nlmsg_type == NLMSG_DONE)
break;
else {
ptr += received_bytes;
msg_len += received_bytes;
}
/* Break if its not a multi part message */
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
break;
}
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
/* parse response */
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
{
/* Get the route data */
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
/* We are just interested in main routing table */
if (route_entry->rtm_table != RT_TABLE_MAIN)
continue;
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
route_attribute_len = RTM_PAYLOAD(nlh);
/* Loop through all attributes */
for ( ; RTA_OK(route_attribute, route_attribute_len);
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
{
switch(route_attribute->rta_type) {
case RTA_OIF:
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
break;
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(route_attribute),
gateway_address, sizeof(gateway_address));
break;
default:
break;
}
}
if ((*gateway_address) && (*interface)) {
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
break;
}
}
close(sock);
return gateway_address;
#endif
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
QString gateway;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
int afinet_type[] = {AF_INET, AF_INET6};
for (int ip_type = 0; ip_type <= 1; ip_type++)
{
mib[3] = afinet_type[ip_type];
size_t needed = 0;
if (sysctl(mib, sizeof(mib) / sizeof(int), nullptr, &needed, nullptr, 0) < 0)
return "";
char* buf;
if ((buf = new char[needed]) == 0)
return "";
if (sysctl(mib, sizeof(mib) / sizeof(int), buf, &needed, nullptr, 0) < 0)
{
qDebug() << "sysctl: net.route.0.0.dump";
delete[] buf;
return gateway;
}
struct rt_msghdr* rt;
for (char* p = buf; p < buf + needed; p += rt->rtm_msglen)
{
rt = reinterpret_cast<struct rt_msghdr*>(p);
struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(rt + 1);
struct sockaddr* sa_tab[RTAX_MAX];
for (int i = 0; i < RTAX_MAX; i++)
{
if (rt->rtm_addrs & (1 << i))
{
sa_tab[i] = sa;
sa = reinterpret_cast<struct sockaddr*>(
reinterpret_cast<char*>(sa) +
((sa->sa_len) > 0 ? (1 + (((sa->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long)));
}
else
{
sa_tab[i] = nullptr;
}
}
if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) == (RTA_DST | RTA_GATEWAY)) &&
sa_tab[RTAX_DST]->sa_family == afinet_type[ip_type] &&
sa_tab[RTAX_GATEWAY]->sa_family == afinet_type[ip_type])
{
if (afinet_type[ip_type] == AF_INET)
{
if ((reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0)
{
char dstStr4[INET_ADDRSTRLEN];
char srcStr4[INET_ADDRSTRLEN];
memcpy(srcStr4,
&(reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_GATEWAY]))->sin_addr,
sizeof(struct in_addr));
if (inet_ntop(AF_INET, srcStr4, dstStr4, INET_ADDRSTRLEN) != nullptr)
gateway = dstStr4;
break;
}
}
else if (afinet_type[ip_type] == AF_INET6)
{
if ((reinterpret_cast<struct sockaddr_in*>(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0)
{
char dstStr6[INET6_ADDRSTRLEN];
char srcStr6[INET6_ADDRSTRLEN];
memcpy(srcStr6,
&(reinterpret_cast<struct sockaddr_in6*>(sa_tab[RTAX_GATEWAY]))->sin6_addr,
sizeof(struct in6_addr));
if (inet_ntop(AF_INET6, srcStr6, dstStr6, INET6_ADDRSTRLEN) != nullptr)
gateway = dstStr6;
break;
}
}
}
}
free(buf);
}
return gateway;
#endif
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef NETWORKUTILITIES_H
#define NETWORKUTILITIES_H
#include <QRegularExpression>
#include <QRegExp>
#include <QString>
class NetworkUtilities : public QObject
{
Q_OBJECT
public:
static QString getIPAddress(const QString &host);
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
static bool checkIPv4Format(const QString &ip);
static bool checkIpSubnetFormat(const QString &ip);
static QString getGatewayAndIface();
static QRegularExpression ipAddressRegExp();
static QRegularExpression ipAddressPortRegExp();
static QRegExp ipAddressWithSubnetRegExp();
static QRegExp ipNetwork24RegExp();
static QRegExp ipPortRegExp();
static QRegExp domainRegExp();
static QString netMaskFromIpWithSubnet(const QString ip);
static QString ipAddressFromIpWithSubnet(const QString ip);
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
};
#endif // NETWORKUTILITIES_H
+3 -1
View File
@@ -13,11 +13,12 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
case DockerContainer::WireGuard: return QLatin1String("wireguard");
case DockerContainer::Awg: return QLatin1String("awg");
case DockerContainer::Ipsec: return QLatin1String("ipsec");
case DockerContainer::Xray: return QLatin1String("xray");
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
case DockerContainer::Dns: return QLatin1String("dns");
case DockerContainer::Sftp: return QLatin1String("sftp");
default: return "";
default: return QString();
}
}
@@ -47,6 +48,7 @@ QString amnezia::scriptName(ProtocolScriptType type)
case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn");
case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf");
case ProtocolScriptType::awg_template: return QLatin1String("template.conf");
case ProtocolScriptType::xray_template: return QLatin1String("template.json");
default: return QString();
}
}
+2 -1
View File
@@ -27,7 +27,8 @@ enum ProtocolScriptType {
container_startup,
openvpn_template,
wireguard_template,
awg_template
awg_template,
xray_template
};
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 8V12" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 16H12.01" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V7.5L14.5 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V8" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 2V8H20" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 15L5 17L9 13" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 581 B

@@ -19,8 +19,10 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
#include "../utilities.h"
#include "leakdetector.h"
#include "logger.h"
#include "core/networkUtilities.h"
namespace {
Logger logger("LinuxRouteMonitor");
@@ -163,7 +165,7 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
if (rtm->rtm_type == RTN_THROW) {
struct in_addr ip4;
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4);
inet_pton(AF_INET, NetworkUtilities::getGatewayAndIface().toUtf8(), &ip4);
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
rtm->rtm_type = RTN_UNICAST;
@@ -221,122 +223,6 @@ void LinuxRouteMonitor::nlsockReady() {
}
}
#define BUFFER_SIZE 4096
QString LinuxRouteMonitor::getgatewayandiface()
{
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
int sock = -1, msgseq = 0;
struct nlmsghdr *nlh, *nlmsg;
struct rtmsg *route_entry;
// This struct contain route attributes (route type)
struct rtattr *route_attribute;
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
char *ptr = buffer;
struct timeval tv;
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
perror("socket failed");
return "";
}
memset(msgbuf, 0, sizeof(msgbuf));
memset(gateway_address, 0, sizeof(gateway_address));
memset(interface, 0, sizeof(interface));
memset(buffer, 0, sizeof(buffer));
/* point the header and the msg structure pointers into the buffer */
nlmsg = (struct nlmsghdr *)msgbuf;
/* Fill in the nlmsg header*/
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
/* 1 Sec Timeout to avoid stall */
tv.tv_sec = 1;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
/* send msg */
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
perror("send failed");
return "";
}
/* receive response */
do
{
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
if (received_bytes < 0) {
perror("Error in recv");
return "";
}
nlh = (struct nlmsghdr *) ptr;
/* Check if the header is valid */
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
(nlmsg->nlmsg_type == NLMSG_ERROR))
{
perror("Error in received packet");
return "";
}
/* If we received all data break */
if (nlh->nlmsg_type == NLMSG_DONE)
break;
else {
ptr += received_bytes;
msg_len += received_bytes;
}
/* Break if its not a multi part message */
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
break;
}
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
/* parse response */
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
{
/* Get the route data */
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
/* We are just interested in main routing table */
if (route_entry->rtm_table != RT_TABLE_MAIN)
continue;
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
route_attribute_len = RTM_PAYLOAD(nlh);
/* Loop through all attributes */
for ( ; RTA_OK(route_attribute, route_attribute_len);
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
{
switch(route_attribute->rta_type) {
case RTA_OIF:
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
break;
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(route_attribute),
gateway_address, sizeof(gateway_address));
break;
default:
break;
}
}
if ((*gateway_address) && (*interface)) {
logger.debug() << "Gateway " << gateway_address << " for interface " << interface;
break;
}
}
close(sock);
return gateway_address;
}
static bool buildAllowedIp(wg_allowedip* ip,
const IPAddress& prefix) {
const char* addrString = qPrintable(prefix.address().toString());
@@ -31,7 +31,6 @@ class LinuxRouteMonitor final : public QObject {
static QString addrToString(const QByteArray& data);
bool rtmSendRoute(int action, int flags, int type,
const IPAddress& prefix);
QString getgatewayandiface();
QString m_ifname;
unsigned int m_ifindex = 0;
int m_nlsock = -1;
-8
View File
@@ -26,7 +26,6 @@ OpenVpnProtocol::~OpenVpnProtocol()
QString OpenVpnProtocol::defaultConfigFileName()
{
// qDebug() << "OpenVpnProtocol::defaultConfigFileName" << defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
return defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
}
@@ -161,7 +160,6 @@ void OpenVpnProtocol::updateRouteGateway(QString line)
ErrorCode OpenVpnProtocol::start()
{
// qDebug() << "Start OpenVPN connection";
OpenVpnProtocol::stop();
if (!QFileInfo::exists(Utils::openVpnExecPath())) {
@@ -196,9 +194,6 @@ ErrorCode OpenVpnProtocol::start()
}
#endif
// QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
// Utils::createEmptyFile(vpnLogFileNamePath);
uint mgmtPort = selectMgmtPort();
qDebug() << "OpenVpnProtocol::start mgmt port selected:" << mgmtPort;
@@ -212,7 +207,6 @@ ErrorCode OpenVpnProtocol::start()
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
if (!m_openVpnProcess) {
// qWarning() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
@@ -242,8 +236,6 @@ ErrorCode OpenVpnProtocol::start()
m_openVpnProcess->start();
// startTimeoutTimer();
return ErrorCode::NoError;
}
+8
View File
@@ -71,6 +71,7 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
{ Proto::Awg, "AmneziaWG" },
{ Proto::Ikev2, "IKEv2" },
{ Proto::L2tp, "L2TP" },
{ Proto::Xray, "XRay" },
{ Proto::TorWebSite, "Website in Tor network" },
{ Proto::Dns, "DNS Service" },
@@ -92,6 +93,7 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
case Proto::WireGuard: return ServiceType::Vpn;
case Proto::Awg: return ServiceType::Vpn;
case Proto::Ikev2: return ServiceType::Vpn;
case Proto::Xray: return ServiceType::Vpn;
case Proto::TorWebSite: return ServiceType::Other;
case Proto::Dns: return ServiceType::Other;
@@ -122,6 +124,7 @@ int ProtocolProps::defaultPort(Proto p)
case Proto::ShadowSocks: return QString(protocols::shadowsocks::defaultPort).toInt();
case Proto::WireGuard: return QString(protocols::wireguard::defaultPort).toInt();
case Proto::Awg: return QString(protocols::awg::defaultPort).toInt();
case Proto::Xray: return QString(protocols::xray::defaultPort).toInt();
case Proto::Ikev2: return -1;
case Proto::L2tp: return -1;
@@ -162,6 +165,8 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
case Proto::Awg: return TransportProto::Udp;
case Proto::Ikev2: return TransportProto::Udp;
case Proto::L2tp: return TransportProto::Udp;
case Proto::Xray: return TransportProto::Tcp;
// non-vpn
case Proto::TorWebSite: return TransportProto::Tcp;
case Proto::Dns: return TransportProto::Udp;
@@ -180,12 +185,15 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
case Proto::Awg: return false;
case Proto::Ikev2: return false;
case Proto::L2tp: return false;
case Proto::Xray: return false;
// non-vpn
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::Sftp: return false;
default: return false;
}
return false;
}
QString ProtocolProps::key_proto_config_data(Proto p)
+16 -1
View File
@@ -82,6 +82,7 @@ namespace amnezia
constexpr char cloak[] = "cloak";
constexpr char sftp[] = "sftp";
constexpr char awg[] = "awg";
constexpr char xray[] = "xray";
constexpr char configVersion[] = "config_version";
@@ -134,6 +135,20 @@ namespace amnezia
constexpr char defaultCipher[] = "chacha20-ietf-poly1305";
}
namespace xray
{
constexpr char serverConfigPath[] = "/opt/amnezia/xray/server.json";
constexpr char uuidPath[] = "/opt/amnezia/xray/xray_uuid.key";
constexpr char PublicKeyPath[] = "/opt/amnezia/xray/xray_public.key";
constexpr char PrivateKeyPath[] = "/opt/amnezia/xray/xray_private.key";
constexpr char shortidPath[] = "/opt/amnezia/xray/xray_short_id.key";
constexpr char defaultSite[] = "www.googletagmanager.com";
constexpr char defaultPort[] = "443";
constexpr char defaultLocalProxyPort[] = "10808";
constexpr char defaultLocalAddr[] = "10.33.0.2";
}
namespace cloak
{
constexpr char ckPublicKeyPath[] = "/opt/amnezia/cloak/cloak_public.key";
@@ -142,7 +157,6 @@ namespace amnezia
constexpr char defaultPort[] = "443";
constexpr char defaultRedirSite[] = "tile.openstreetmap.org";
constexpr char defaultCipher[] = "chacha20-poly1305";
}
namespace wireguard
@@ -206,6 +220,7 @@ namespace amnezia
Awg,
Ikev2,
L2tp,
Xray,
// non-vpn
TorWebSite,
+2
View File
@@ -9,6 +9,7 @@
#include "openvpnprotocol.h"
#include "shadowsocksvpnprotocol.h"
#include "wireguardprotocol.h"
#include "xrayprotocol.h"
#endif
#ifdef Q_OS_WINDOWS
@@ -114,6 +115,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
case DockerContainer::Awg: return new WireguardProtocol(configuration);
case DockerContainer::Xray: return new XrayProtocol(configuration);
#endif
default: return nullptr;
}
+231
View File
@@ -0,0 +1,231 @@
#include "xrayprotocol.h"
#include "utilities.h"
#include "containers/containers_defs.h"
#include "core/networkUtilities.h"
#include <QCryptographicHash>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkInterface>
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent):
VpnProtocol(configuration, parent)
{
readXrayConfiguration(configuration);
m_routeGateway = NetworkUtilities::getGatewayAndIface();
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
}
XrayProtocol::~XrayProtocol()
{
XrayProtocol::stop();
QThread::msleep(200);
m_xrayProcess.close();
}
ErrorCode XrayProtocol::start()
{
qDebug().noquote() << "XrayProtocol xrayExecPath():" << xrayExecPath();
if (!QFileInfo::exists(xrayExecPath())) {
setLastError(ErrorCode::XrayExecutableMissing);
return lastError();
}
if (Utils::processIsRunning(Utils::executable(xrayExecPath(), true))) {
Utils::killProcessByName(Utils::executable(xrayExecPath(), true));
}
#ifdef QT_DEBUG
m_xrayCfgFile.setAutoRemove(false);
#endif
m_xrayCfgFile.open();
m_xrayCfgFile.write(QJsonDocument(m_xrayConfig).toJson());
m_xrayCfgFile.close();
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
qDebug().noquote() << "XrayProtocol::start()"
<< xrayExecPath() << args.join(" ");
m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels);
m_xrayProcess.setProgram(xrayExecPath());
m_xrayProcess.setArguments(args);
connect(&m_xrayProcess, &QProcess::readyReadStandardOutput, this, [this]() {
#ifdef QT_DEBUG
qDebug().noquote() << "xray:" << m_xrayProcess.readAllStandardOutput();
#endif
});
connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "XrayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(Vpn::ConnectionState::Disconnected);
if (exitStatus != QProcess::NormalExit) {
emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
stop();
}
if (exitCode != 0) {
emit protocolError(amnezia::ErrorCode::InternalError);
stop();
}
});
m_xrayProcess.start();
m_xrayProcess.waitForStarted();
if (m_xrayProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(Vpn::ConnectionState::Connecting);
QThread::msleep(1000);
return startTun2Sock();
}
else return ErrorCode::XrayExecutableMissing;
}
ErrorCode XrayProtocol::startTun2Sock()
{
if (!QFileInfo::exists(Utils::tun2socksPath())) {
setLastError(ErrorCode::Tun2SockExecutableMissing);
return lastError();
}
m_t2sProcess = IpcClient::CreatePrivilegedProcess();
if (!m_t2sProcess) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_t2sProcess->waitForSource(1000);
if (!m_t2sProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
QString XrayConStr = "socks5://127.0.0.1:" + QString::number(m_localPort);
m_t2sProcess->setProgram(PermittedProcess::Tun2Socks);
#ifdef Q_OS_WIN
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr, "-tun-post-up",
QString("cmd /c netsh interface ip set address name=\"tun2\" static %1 255.255.255.255").arg(amnezia::protocols::xray::defaultLocalAddr)});
#endif
#ifdef Q_OS_LINUX
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr});
#endif
#ifdef Q_OS_MAC
QStringList arguments({"-device", "utun22", "-proxy", XrayConStr});
#endif
m_t2sProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_t2sProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_t2sProcess.data(), &PrivilegedProcess::stateChanged,
[&](QProcess::ProcessState newState) {
qDebug() << "PrivilegedProcess stateChanged" << newState;
if (newState == QProcess::Running)
{
setConnectionState(Vpn::ConnectionState::Connecting);
QList<QHostAddress> dnsAddr;
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
#ifdef Q_OS_MACOS
QThread::msleep(5000);
IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
#endif
#ifdef Q_OS_WINDOWS
QThread::msleep(15000);
#endif
#ifdef Q_OS_LINUX
QThread::msleep(1000);
IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
#endif
if (m_routeMode == 0) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
}
IpcClient::Interface()->StopRoutingIpv6();
#ifdef Q_OS_WIN
IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
for (int i = 0; i < netInterfaces.size(); i++) {
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
IpcClient::Interface()->enablePeerTraffic(m_configData);
}
}
}
#endif
setConnectionState(Vpn::ConnectionState::Connected);
}
});
#if !defined(Q_OS_MACOS)
connect(m_t2sProcess.data(), &PrivilegedProcess::finished, this,
[&]() {
setConnectionState(Vpn::ConnectionState::Disconnected);
IpcClient::Interface()->deleteTun("tun2");
IpcClient::Interface()->StartRoutingIpv6();
IpcClient::Interface()->clearSavedRoutes();
});
#endif
m_t2sProcess->start();
return ErrorCode::NoError;
}
void XrayProtocol::stop()
{
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->disableKillSwitch();
#endif
qDebug() << "XrayProtocol::stop()";
m_xrayProcess.terminate();
if (m_t2sProcess) {
m_t2sProcess->close();
}
#ifdef Q_OS_WIN
Utils::signalCtrl(m_xrayProcess.processId(), CTRL_C_EVENT);
#endif
}
QString XrayProtocol::xrayExecPath()
{
#ifdef Q_OS_WIN
return Utils::executable(QString("xray/xray"), true);
#else
return Utils::executable(QString("xray"), true);
#endif
}
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
{
m_configData = configuration;
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
m_xrayConfig = xrayConfiguration;
m_localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort).toInt();
m_remoteAddress = configuration.value(amnezia::config_key::hostName).toString();
m_routeMode = configuration.value(amnezia::config_key::splitTunnelType).toInt();
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef XRAYPROTOCOL_H
#define XRAYPROTOCOL_H
#include "openvpnprotocol.h"
#include "QProcess"
#include "containers/containers_defs.h"
class XrayProtocol : public VpnProtocol
{
public:
XrayProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~XrayProtocol() override;
ErrorCode start() override;
ErrorCode startTun2Sock();
void stop() override;
protected:
void readXrayConfiguration(const QJsonObject &configuration);
protected:
QJsonObject m_xrayConfig;
private:
static QString xrayExecPath();
static QString tun2SocksExecPath();
private:
int m_localPort;
QString m_remoteAddress;
int m_routeMode;
QJsonObject m_configData;
QString m_primaryDNS;
QString m_secondaryDNS;
#ifndef Q_OS_IOS
QProcess m_xrayProcess;
QSharedPointer<PrivilegedProcess> m_t2sProcess;
#endif
QTemporaryFile m_xrayCfgFile;
};
#endif // XRAYPROTOCOL_H
+9
View File
@@ -198,6 +198,7 @@
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
@@ -224,9 +225,17 @@
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>images/controls/close.svg</file>
<file>images/controls/search.svg</file>
<file>server_scripts/xray/configure_container.sh</file>
<file>server_scripts/xray/Dockerfile</file>
<file>server_scripts/xray/run_container.sh</file>
<file>server_scripts/xray/start.sh</file>
<file>server_scripts/xray/template.json</file>
<file>ui/qml/Pages2/PageProtocolWireGuardSettings.qml</file>
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
<file>images/controls/split-tunneling.svg</file>
<file>ui/qml/Controls2/DrawerType2.qml</file>
<file>images/controls/alert-circle.svg</file>
<file>images/controls/file-check-2.svg</file>
<file>ui/qml/Controls2/WarningType.qml</file>
</qresource>
</RCC>
+53
View File
@@ -0,0 +1,53 @@
FROM alpine:3.15
LABEL maintainer="AmneziaVPN"
ARG XRAY_RELEASE="v1.8.6"
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
RUN apk --update upgrade --no-cache
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 mkdir -p /opt/amnezia/xray
RUN curl -L https://github.com/XTLS/Xray-core/releases/download/${XRAY_RELEASE}/Xray-linux-64.zip > /root/xray.zip;\
unzip /root/xray.zip -d /usr/bin/;\
chmod a+x /usr/bin/xray;
# 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\
net.core.default_qdisc=fq \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 = bbr \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
ENV TZ=Asia/Shanghai
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
@@ -0,0 +1,67 @@
cd /opt/amnezia/xray
XRAY_CLIENT_ID=$(xray uuid) && echo $XRAY_CLIENT_ID > /opt/amnezia/xray/xray_uuid.key
XRAY_SHORT_ID=$(openssl rand -hex 8) && echo $XRAY_SHORT_ID > /opt/amnezia/xray/xray_short_id.key
KEYPAIR=$(xray x25519)
LINE_NUM=1
while IFS= read -r line; do
if [[ $LINE_NUM -gt 1 ]]
then
IFS=":" read FIST XRAY_PUBLIC_KEY <<< "$line"
else
LINE_NUM=$((LINE_NUM + 1))
IFS=":" read FIST XRAY_PRIVATE_KEY <<< "$line"
fi
done <<< "$KEYPAIR"
XRAY_PRIVATE_KEY=$(echo $XRAY_PRIVATE_KEY | tr -d ' ')
XRAY_PUBLIC_KEY=$(echo $XRAY_PUBLIC_KEY | tr -d ' ')
echo $XRAY_PUBLIC_KEY > /opt/amnezia/xray/xray_public.key
echo $XRAY_PRIVATE_KEY > /opt/amnezia/xray/xray_private.key
cat > /opt/amnezia/xray/server.json <<EOF
{
"log": {
"loglevel": "error"
},
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "$XRAY_CLIENT_ID",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"dest": "$XRAY_SITE_NAME:443",
"serverNames": [
"$XRAY_SITE_NAME"
],
"privateKey": "$XRAY_PRIVATE_KEY",
"shortIds": [
"$XRAY_SHORT_ID"
]
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}
EOF
@@ -0,0 +1,17 @@
# Run container
sudo docker run -d \
--privileged \
--log-driver none \
--restart always \
--cap-add=NET_ADMIN \
-p 443:443/tcp \
--name $CONTAINER_NAME $CONTAINER_NAME
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
# Create tun device if not exist
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
# 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"
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
echo "Container startup"
ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -P INPUT DROP
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -P INPUT DROP
# kill daemons in case of restart
killall -KILL xray
# start daemons if configured
if [ -f /opt/amnezia/xray/server.json ]; then (xray -config /opt/amnezia/xray/server.json); fi
tail -f /dev/null
+46
View File
@@ -0,0 +1,46 @@
{
"log": {
"loglevel": "error"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "$SERVER_IP_ADDRESS",
"port": 443,
"users": [
{
"id": "$XRAY_CLIENT_ID",
"flow": "xtls-rprx-vision",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"fingerprint": "chrome",
"serverName": "$XRAY_SITE_NAME",
"publicKey": "$XRAY_PUBLIC_KEY",
"shortId": "$XRAY_SHORT_ID",
"spiderX": ""
}
}
}
]
}
+3 -3
View File
@@ -3,7 +3,7 @@
#include "QThread"
#include "QCoreApplication"
#include "utilities.h"
#include "core/networkUtilities.h"
#include "version.h"
#include "containers/containers_defs.h"
@@ -288,9 +288,9 @@ QStringList Settings::getVpnIps(RouteMode mode) const
QStringList ips;
const QVariantMap &m = vpnSites(mode);
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
if (Utils::checkIpSubnetFormat(i.key())) {
if (NetworkUtilities::checkIpSubnetFormat(i.key())) {
ips.append(i.key());
} else if (Utils::checkIpSubnetFormat(i.value().toString())) {
} else if (NetworkUtilities::checkIpSubnetFormat(i.value().toString())) {
ips.append(i.value().toString());
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -337,6 +337,38 @@ void ExportController::generateCloakConfig()
emit exportConfigChanged();
}
void ExportController::generateXrayConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString clientId;
QString config =
m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, Proto::Xray, clientId, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Xray, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
emit exportConfigChanged();
}
QString ExportController::getConfig()
{
return m_config;
+1
View File
@@ -37,6 +37,7 @@ public slots:
void generateAwgConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();
void generateXrayConfig();
QString getConfig();
QString getNativeConfigString();
@@ -19,6 +19,7 @@ namespace
Amnezia,
OpenVpn,
WireGuard,
Xray,
Backup,
Invalid
};
@@ -34,6 +35,9 @@ namespace
const QString wireguardConfigPatternSectionInterface = "[Interface]";
const QString wireguardConfigPatternSectionPeer = "[Peer]";
const QString xrayConfigPatternInbound = "inbounds";
const QString xrayConfigPatternOutbound = "outbounds";
const QString amneziaConfigPattern = "containers";
const QString amneziaFreeConfigPattern = "api_key";
const QString backupPattern = "Servers/serversList";
@@ -49,6 +53,8 @@ namespace
} else if (config.contains(wireguardConfigPatternSectionInterface)
&& config.contains(wireguardConfigPatternSectionPeer)) {
return ConfigTypes::WireGuard;
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
return ConfigTypes::Xray;
}
return ConfigTypes::Invalid;
}
@@ -109,6 +115,10 @@ bool ImportController::extractConfigFromData(QString data)
m_config = extractWireGuardConfig(config);
return m_config.empty() ? false : true;
}
case ConfigTypes::Xray: {
m_config = extractXrayConfig(config);
return m_config.empty() ? false : true;
}
case ConfigTypes::Amnezia: {
m_config = QJsonDocument::fromJson(config.toUtf8()).object();
return m_config.empty() ? false : true;
@@ -349,6 +359,42 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
return config;
}
QJsonObject ImportController::extractXrayConfig(const QString &data)
{
QJsonParseError parserErr;
QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr);
QJsonObject xrayVpnConfig;
xrayVpnConfig[config_key::config] = jsonConf.toJson().constData();
QJsonObject lastConfig;
lastConfig[config_key::last_config] = jsonConf.toJson().constData();
lastConfig[config_key::isThirdPartyConfig] = true;
QJsonObject containers;
containers.insert(config_key::container, QJsonValue("amnezia-xray"));
containers.insert(config_key::xray, QJsonValue(lastConfig));
QJsonArray arr;
arr.push_back(containers);
QString hostName;
const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)");
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
if (hostNameMatch.hasMatch()) {
hostName = hostNameMatch.captured(1);
}
QJsonObject config;
config[config_key::containers] = arr;
config[config_key::defaultContainer] = "amnezia-xray";
config[config_key::description] = m_settings->nextAvailableServerName();
config[config_key::hostName] = hostName;
return config;
}
#ifdef Q_OS_ANDROID
static QMutex qrDecodeMutex;
+1
View File
@@ -47,6 +47,7 @@ signals:
private:
QJsonObject extractOpenVpnConfig(const QString &data);
QJsonObject extractWireGuardConfig(const QString &data);
QJsonObject extractXrayConfig(const QString &data);
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void stopDecodingQr();
+3 -3
View File
@@ -9,6 +9,7 @@
#include "core/errorstrings.h"
#include "core/controllers/serverController.h"
#include "core/networkUtilities.h"
#include "utilities.h"
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
@@ -352,12 +353,12 @@ void InstallController::removeCurrentlyProcessedContainer()
QRegularExpression InstallController::ipAddressPortRegExp()
{
return Utils::ipAddressPortRegExp();
return NetworkUtilities::ipAddressPortRegExp();
}
QRegularExpression InstallController::ipAddressRegExp()
{
return Utils::ipAddressRegExp();
return NetworkUtilities::ipAddressRegExp();
}
void InstallController::setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName,
@@ -450,7 +451,6 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
process->write((password + "\n").toUtf8());
}
// qDebug().noquote() << "onPushButtonSftpMountDriveClicked" << args;
#endif
}
+2
View File
@@ -48,6 +48,7 @@ namespace PageLoader
PageProtocolOpenVpnSettings,
PageProtocolShadowSocksSettings,
PageProtocolCloakSettings,
PageProtocolXraySettings,
PageProtocolWireGuardSettings,
PageProtocolAwgSettings,
PageProtocolIKev2Settings,
@@ -109,6 +110,7 @@ signals:
void showBusyIndicator(bool visible);
void disableControls(bool disabled);
void disableTabBar(bool disabled);
void hideMainWindow();
void raiseMainWindow();
+5 -5
View File
@@ -5,7 +5,7 @@
#include <QStandardPaths>
#include "systemController.h"
#include "utilities.h"
#include "core/networkUtilities.h"
SitesController::SitesController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<VpnConnection> &vpnConnection,
@@ -25,7 +25,7 @@ void SitesController::addSite(QString hostname)
return;
}
if (!Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
// get domain name if it present
hostname.replace("https://", "");
hostname.replace("http://", "");
@@ -40,7 +40,7 @@ void SitesController::addSite(QString hostname)
if (!ip.isEmpty()) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << ip));
} else if (Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname));
}
@@ -57,7 +57,7 @@ void SitesController::addSite(QString hostname)
}
};
if (Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
processSite(hostname, "");
} else {
processSite(hostname, "");
@@ -110,7 +110,7 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
auto hostname = jsonObject.value("hostname").toString("");
auto ip = jsonObject.value("ip").toString("");
if (!hostname.contains(".") && !Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
qDebug() << hostname << " not look like ip adress or domain name";
continue;
}
@@ -0,0 +1,69 @@
#include "xrayConfigModel.h"
#include "protocols/protocols_defs.h"
XrayConfigModel::XrayConfigModel(QObject *parent) : QAbstractListModel(parent)
{
}
int XrayConfigModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
return false;
}
switch (role) {
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
}
emit dataChanged(index, index, QList { role });
return true;
}
QVariant XrayConfigModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return false;
}
switch (role) {
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite);
}
return QVariant();
}
void XrayConfigModel::updateModel(const QJsonObject &config)
{
beginResetModel();
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config;
QJsonObject protocolConfig = config.value(config_key::xray).toObject();
m_protocolConfig.insert(config_key::site,
protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite));
endResetModel();
}
QJsonObject XrayConfigModel::getConfig()
{
m_fullConfig.insert(config_key::xray, m_protocolConfig);
return m_fullConfig;
}
QHash<int, QByteArray> XrayConfigModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[SiteRole] = "site";
return roles;
}
@@ -0,0 +1,38 @@
#ifndef XRAYCONFIGMODEL_H
#define XRAYCONFIGMODEL_H
#include <QAbstractListModel>
#include <QJsonObject>
#include "containers/containers_defs.h"
class XrayConfigModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
SiteRole
};
explicit XrayConfigModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateModel(const QJsonObject &config);
QJsonObject getConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
DockerContainer m_container;
QJsonObject m_protocolConfig;
QJsonObject m_fullConfig;
};
#endif // XRAYCONFIGMODEL_H
+2
View File
@@ -79,6 +79,8 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardSettings;
case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings;
case Proto::L2tp: return PageLoader::PageEnum::PageProtocolIKev2Settings;
case Proto::Xray: return PageLoader::PageEnum::PageProtocolXraySettings;
// non-vpn
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
@@ -57,6 +57,12 @@ ListView {
PageController.goToPage(PageEnum.PageProtocolOpenVpnSettings)
break
}
case ContainerEnum.Xray: {
XrayConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolXraySettings)
break
}
case ContainerEnum.WireGuard: {
WireGuardConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolWireGuardSettings)
@@ -89,12 +89,12 @@ RadioButton {
}
contentItem: Item {
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
anchors.fill: parent
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 8 + background.width
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
+51
View File
@@ -0,0 +1,51 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "TextTypes"
Rectangle {
property string textColor: "#D7D8DB"
property string backGroundColor: "#1C1D21"
property string textString
property string iconPath
property real iconWidth: 16
property real iconHeight: 16
color: backGroundColor
radius: 8
implicitHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
RowLayout {
id: content
width: parent.width
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
anchors.topMargin: 8
anchors.bottomMargin: 8
spacing: 0
Image {
Layout.alignment: Qt.AlignTop
width: iconWidth
height: iconHeight
source: iconPath
}
CaptionTextType {
id: supportingText
Layout.fillWidth: true
Layout.leftMargin: 8
text: textString
color: textColor
}
}
}
+2 -2
View File
@@ -14,8 +14,8 @@ import "../Config"
PageType {
id: root
Component.onCompleted: PageController.disableControls(true)
Component.onDestruction: PageController.disableControls(false)
Component.onCompleted: PageController.disableTabBar(true)
Component.onDestruction: PageController.disableTabBar(false)
SortFilterProxyModel {
id: proxyServersModel
+84 -102
View File
@@ -299,129 +299,111 @@ PageType {
}
}
Flickable {
id: serversContainer
ButtonGroup {
id: serversRadioButtonGroup
}
ListView {
id: serversMenuContent
anchors.top: serversMenuHeader.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 16
contentHeight: col.height + col.anchors.bottomMargin
implicitHeight: parent.height - serversMenuHeader.implicitHeight
clip: true
model: ServersModel
currentIndex: ServersModel.defaultIndex
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: serversContainer.height >= serversContainer.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
policy: serversMenuContent.height >= serversMenuContent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 32
spacing: 16
ButtonGroup {
id: serversRadioButtonGroup
Connections {
target: ServersModel
function onDefaultServerIndexChanged(serverIndex) {
serversMenuContent.currentIndex = serverIndex
}
}
ListView {
id: serversMenuContent
width: parent.width
height: serversMenuContent.contentItem.height
clip: true
model: ServersModel
currentIndex: ServersModel.defaultIndex
delegate: Item {
id: menuContentDelegate
Connections {
target: ServersModel
function onDefaultServerIndexChanged(serverIndex) {
serversMenuContent.currentIndex = serverIndex
property variant delegateData: model
implicitWidth: serversMenuContent.width
implicitHeight: serverRadioButtonContent.implicitHeight
ColumnLayout {
id: serverRadioButtonContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 0
RowLayout {
Layout.fillWidth: true
VerticalRadioButton {
id: serverRadioButton
Layout.fillWidth: true
text: name
descriptionText: serverDescription
checked: index === serversMenuContent.currentIndex
checkable: !ConnectionController.isConnected
ButtonGroup.group: serversRadioButtonGroup
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
return
}
serversMenuContent.currentIndex = index
ServersModel.defaultIndex = index
}
MouseArea {
anchors.fill: serverRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
ImageButtonType {
image: "qrc:/images/controls/settings.svg"
imageColor: "#D7D8DB"
implicitWidth: 56
implicitHeight: 56
z: 1
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
drawer.close()
}
}
}
clip: true
interactive: false
delegate: Item {
id: menuContentDelegate
property variant delegateData: model
implicitWidth: serversMenuContent.width
implicitHeight: serverRadioButtonContent.implicitHeight
ColumnLayout {
id: serverRadioButtonContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 0
RowLayout {
VerticalRadioButton {
id: serverRadioButton
Layout.fillWidth: true
text: name
descriptionText: serverDescription
checked: index === serversMenuContent.currentIndex
checkable: !ConnectionController.isConnected
ButtonGroup.group: serversRadioButtonGroup
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
return
}
serversMenuContent.currentIndex = index
ServersModel.defaultIndex = index
}
MouseArea {
anchors.fill: serverRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
ImageButtonType {
image: "qrc:/images/controls/settings.svg"
imageColor: "#D7D8DB"
implicitWidth: 56
implicitHeight: 56
z: 1
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
drawer.close()
}
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.rightMargin: 0
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.rightMargin: 0
}
}
}
@@ -325,7 +325,7 @@ PageType {
onClicked: {
var headerText = qsTr("Remove AmneziaWG from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
@@ -359,15 +359,24 @@ PageType {
text: qsTr("Save")
clickedFunc: function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
onClicked: {
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
}
}
}
}
@@ -382,7 +382,7 @@ PageType {
clickedFunc: function() {
var headerText = qsTr("Remove OpenVpn from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
+1 -1
View File
@@ -182,7 +182,7 @@ PageType {
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
@@ -41,7 +41,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
@@ -126,21 +126,20 @@ PageType {
text: qsTr("Remove WG")
onClicked: {
questionDrawer.headerText = qsTr("Remove WG from server?")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("Remove WG from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -0,0 +1,155 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerEnum 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
ColumnLayout {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: XrayConfigModel
delegate: Item {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
HeaderType {
Layout.fillWidth: true
headerText: qsTr("XRay settings")
}
TextFieldWithHeaderType {
Layout.fillWidth: true
Layout.topMargin: 32
headerText: qsTr("Disguised as traffic from")
textFieldText: site
textField.onEditingFinished: {
if (textFieldText !== site) {
var tmpText = textFieldText
tmpText = tmpText.toLocaleLowerCase()
var indexHttps = tmpText.indexOf("https://")
if (indexHttps === 0) {
tmpText = textFieldText.substring(8)
} else {
site = textFieldText
}
}
}
}
BasicButtonType {
Layout.topMargin: 24
Layout.leftMargin: -8
implicitHeight: 32
visible: ContainersModel.getCurrentlyProcessedContainerIndex() === ContainerEnum.Xray
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: "#EB5757"
text: qsTr("Remove XRay")
clickedFunc: function() {
var headerText = qsTr("Remove XRay from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save and Restart Amnezia")
onClicked: {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(XrayConfigModel.getConfig())
}
}
}
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}
@@ -121,7 +121,7 @@ PageType {
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Login")
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
+20 -35
View File
@@ -68,44 +68,10 @@ PageType {
height: 20
font.pixelSize: 14
text: qsTr("This is a free and open source application. If you like it, support the developers with a donation. ") +
qsTr("And if you dont like the application, all the more reason to support it - the donation will be used for the improving the application.")
text: qsTr("Amnezia is a free and open-source application. You can support the developers if you like it.")
color: "#CCCAC8"
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Card on Patreon")
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://www.patreon.com/amneziavpn"))
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
borderWidth: 1
text: qsTr("Show other methods on Github")
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client#donate"))
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
@@ -197,6 +163,25 @@ PageType {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}
BasicButtonType {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16
Layout.topMargin: -15
implicitHeight: 25
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#FBB26A"
text: qsTr("Privacy Policy")
clickedFunc: function() {
Qt.openUrlExternally("https://amnezia.org/en/policy")
}
}
}
}
}
+7 -11
View File
@@ -63,22 +63,18 @@ PageType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("Backup")
headerText: qsTr("Back up your configuration")
descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
}
ListItemTitleType {
WarningType {
Layout.topMargin: 16
Layout.fillWidth: true
Layout.topMargin: 10
text: qsTr("Configuration backup")
}
textString: qsTr("The backup will contain your passwords and private keys for all servers added " +
"to AmneziaVPN. Keep this information in a secure place.")
CaptionTextType {
Layout.fillWidth: true
Layout.topMargin: -12
text: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
color: "#878B91"
iconPath: "qrc:/images/controls/alert-circle.svg"
}
BasicButtonType {
@@ -57,6 +57,8 @@ disabled after 14 days, and all log files will be deleted.")
Layout.fillWidth: true
headerText: qsTr("Logging")
descriptionText: qsTr("Enabling this function will save application's logs automatically, " +
"By default, logging functionality is disabled. Enable log saving in case of application malfunction.")
}
SwitcherType {
@@ -166,7 +166,7 @@ PageType {
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Remove this server from the app")
text: qsTr("Remove server from application")
textColor: "#EB5757"
clickedFunction: function() {
@@ -196,12 +196,12 @@ PageType {
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Clear server Amnezia-installed services")
text: qsTr("Clear server from Amnezia software")
textColor: "#EB5757"
clickedFunction: function() {
var headerText = qsTr("Do you want to clear server Amnezia-installed services?")
var descriptionText = qsTr("All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.")
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
@@ -163,7 +163,7 @@ PageType {
}
TabButtonType {
isSelected: tabBar.currentIndex === 2
text: qsTr("Data")
text: qsTr("Management")
}
}
@@ -84,6 +84,7 @@ PageType {
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
@@ -69,8 +69,8 @@ PageType {
leftImageSource: "qrc:/images/controls/folder-open.svg"
clickedFunction: function() {
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.backup)" :
"Config files (*.vpn *.ovpn *.conf)"
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
"Config files (*.vpn *.ovpn *.conf *.json)"
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
if (fileName !== "") {
if (ImportController.extractConfigFromFile(fileName)) {
@@ -67,7 +67,7 @@ PageType {
id: username
Layout.fillWidth: true
headerText: qsTr("Login to connect via SSH")
headerText: qsTr("SSH Username")
textFieldPlaceholderText: "root"
textField.onFocusChanged: {
+2 -3
View File
@@ -136,8 +136,7 @@ PageType {
CardType {
implicitWidth: parent.width
headerText: qsTr("Set up a VPN yourself")
bodyText: qsTr("I want to choose a VPN protocol")
headerText: qsTr("Choose a VPN protocol")
ButtonGroup.group: buttonGroup
@@ -194,7 +193,7 @@ PageType {
return true
}
text: qsTr("Set up later")
text: qsTr("Skip setup")
clickedFunc: function() {
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
@@ -14,8 +14,8 @@ import "../Config"
PageType {
id: root
Component.onCompleted: PageController.disableControls(true)
Component.onDestruction: PageController.disableControls(false)
Component.onCompleted: PageController.disableTabBar(true)
Component.onDestruction: PageController.disableTabBar(false)
property bool isTimerRunning: true
property string progressBarText: qsTr("Usually it takes no more than 5 minutes")
@@ -48,6 +48,10 @@ PageType {
isControlsDisabled = disabled
}
function onDisableTabBar(disabled) {
isControlsDisabled = disabled
}
function onEscapePressed() {
if (isControlsDisabled) {
return
@@ -74,7 +74,7 @@ PageType {
visible: fileName.text !== ""
Image {
source: "qrc:/images/controls/file-cog-2.svg"
source: "qrc:/images/controls/file-check-2.svg"
}
Header2TextType {
@@ -87,14 +87,6 @@ PageType {
}
}
CaptionTextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Do not use connection codes from untrusted sources, as they may be created to intercept your data.")
color: "#878B91"
}
BasicButtonType {
Layout.topMargin: 16
Layout.leftMargin: -8
@@ -113,6 +105,15 @@ PageType {
}
}
WarningType {
Layout.topMargin: 16
Layout.fillWidth: true
textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.")
iconPath: "qrc:/images/controls/alert-circle.svg"
}
Rectangle {
Layout.fillWidth: true
Layout.bottomMargin: 16
+16 -1
View File
@@ -24,7 +24,8 @@ PageType {
WireGuard,
Awg,
ShadowSocks,
Cloak
Cloak,
Xray
}
signal revokeConfig(int index)
@@ -88,6 +89,13 @@ PageType {
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break
}
case PageShare.ConfigType.Xray: {
ExportController.generateXrayConfig()
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_xray"
break
}
}
PageController.showBusyIndicator(false)
@@ -136,6 +144,11 @@ PageType {
property string name: qsTr("Cloak native format")
property var type: PageShare.ConfigType.Cloak
}
QtObject {
id: xrayConnectionFormat
property string name: qsTr("XRay native format")
property var type: PageShare.ConfigType.Xray
}
FlickableType {
anchors.top: parent.top
@@ -435,6 +448,8 @@ PageType {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
root.connectionTypesModel.push(cloakConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-xray")) {
root.connectionTypesModel.push(xrayConnectionFormat)
}
}
}
+7 -2
View File
@@ -15,6 +15,7 @@ PageType {
id: root
property bool isControlsDisabled: false
property bool isTabBarDisabled: false
Connections {
target: PageController
@@ -38,6 +39,10 @@ PageType {
isControlsDisabled = disabled
}
function onDisableTabBar(disabled) {
isTabBarDisabled = disabled
}
function onClosePage() {
if (tabBarStackView.depth <= 1) {
return
@@ -63,7 +68,7 @@ PageType {
}
function onEscapePressed() {
if (root.isControlsDisabled) {
if (root.isControlsDisabled || root.isTabBarDisabled) {
return
}
@@ -179,7 +184,7 @@ PageType {
leftPadding: 96
rightPadding: 96
enabled: !root.isControlsDisabled
enabled: !root.isControlsDisabled && !root.isTabBarDisabled
background: Shape {
width: parent.width
+13 -95
View File
@@ -1,8 +1,6 @@
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QHostAddress>
#include <QHostInfo>
#include <QProcess>
#include <QRandomGenerator>
#include <QRegularExpression>
@@ -118,60 +116,6 @@ bool Utils::processIsRunning(const QString &fileName)
#endif
}
QString Utils::getIPAddress(const QString &host)
{
if (ipAddressRegExp().match(host).hasMatch()) {
return host;
}
QList<QHostAddress> addresses = QHostInfo::fromName(host).addresses();
if (!addresses.isEmpty()) {
return addresses.first().toString();
}
qDebug() << "Unable to resolve address for " << host;
return "";
}
QString Utils::getStringBetween(const QString &s, const QString &a, const QString &b)
{
int ap = s.indexOf(a), bp = s.indexOf(b, ap + a.length());
if (ap < 0 || bp < 0)
return QString();
ap += a.length();
if (bp - ap <= 0)
return QString();
return s.mid(ap, bp - ap).trimmed();
}
bool Utils::checkIPv4Format(const QString &ip)
{
if (ip.isEmpty())
return false;
int count = ip.count(".");
if (count != 3)
return false;
QHostAddress addr(ip);
return (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol);
}
bool Utils::checkIpSubnetFormat(const QString &ip)
{
if (!ip.contains("/"))
return checkIPv4Format(ip);
QStringList parts = ip.split("/");
if (parts.size() != 2)
return false;
bool ok;
int subnet = parts.at(1).toInt(&ok);
if (subnet >= 0 && subnet <= 32 && ok)
return checkIPv4Format(parts.at(0));
else
return false;
}
void Utils::killProcessByName(const QString &name)
{
qDebug().noquote() << "Kill process" << name;
@@ -184,45 +128,6 @@ void Utils::killProcessByName(const QString &name)
#endif
}
QString Utils::netMaskFromIpWithSubnet(const QString ip)
{
if (!ip.contains("/"))
return "255.255.255.255";
bool ok;
int prefix = ip.split("/").at(1).toInt(&ok);
if (!ok)
return "255.255.255.255";
unsigned long mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;
return QString("%1.%2.%3.%4").arg(mask >> 24).arg((mask >> 16) & 0xFF).arg((mask >> 8) & 0xFF).arg(mask & 0xFF);
}
QString Utils::ipAddressFromIpWithSubnet(const QString ip)
{
if (ip.count(".") != 3)
return "";
return ip.split("/").first();
}
QStringList Utils::summarizeRoutes(const QStringList &ips, const QString cidr)
{
// QMap<int, int>
// QHostAddress
// QMap<QString, QStringList> subnets; // <"a.b", <list subnets>>
// for (const QString &ip : ips) {
// if (ip.count(".") != 3) continue;
// const QStringList &parts = ip.split(".");
// subnets[parts.at(0) + "." + parts.at(1)].append(ip);
// }
return QStringList();
}
QString Utils::openVpnExecPath()
{
#ifdef Q_OS_WIN
@@ -259,6 +164,19 @@ QString Utils::certUtilPath()
#endif
}
QString Utils::tun2socksPath()
{
#ifdef Q_OS_WIN
return Utils::executable("xray/tun2socks", true);
#elif defined Q_OS_LINUX
// We have service that runs OpenVPN on Linux. We need to make same
// path for client and service.
return Utils::executable("../../client/bin/tun2socks", true);
#else
return Utils::executable("/tun2socks", true);
#endif
}
#ifdef Q_OS_WIN
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
+1 -41
View File
@@ -22,53 +22,13 @@ public:
static bool createEmptyFile(const QString &path);
static bool initializePath(const QString &path);
static QString getIPAddress(const QString &host);
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
static bool checkIPv4Format(const QString &ip);
static bool checkIpSubnetFormat(const QString &ip);
static QRegularExpression ipAddressRegExp()
{
return QRegularExpression("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$");
}
static QRegularExpression ipAddressPortRegExp()
{
return QRegularExpression("^(?:(?: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 QRegExp ipAddressWithSubnetRegExp()
{
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,2}){0,1}");
}
static QRegExp ipNetwork24RegExp()
{
return QRegExp("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}"
"0$");
}
static QRegExp ipPortRegExp()
{
return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$");
}
static QRegExp domainRegExp()
{
return QRegExp("(((?!\\-))(xn\\-\\-)?[a-z0-9\\-_]{0,61}[a-z0-9]{1,1}\\.)*(xn\\-\\-)?([a-z0-9\\-]{1,61}|[a-z0-"
"9\\-]{1,30})\\.[a-z]{2,}");
}
static bool processIsRunning(const QString &fileName);
static void killProcessByName(const QString &name);
static QString netMaskFromIpWithSubnet(const QString ip);
static QString ipAddressFromIpWithSubnet(const QString ip);
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
static QString openVpnExecPath();
static QString wireguardExecPath();
static QString certUtilPath();
static QString tun2socksPath();
#ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
+10 -5
View File
@@ -27,7 +27,7 @@
#include "platforms/ios/ios_controller.h"
#endif
#include "utilities.h"
#include "core/networkUtilities.h"
#include "vpnconnection.h"
VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator,
@@ -59,6 +59,8 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{
#ifdef AMNEZIA_DESKTOP
QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
if (IpcClient::Interface()) {
if (state == Vpn::ConnectionState::Connected) {
IpcClient::Interface()->resetIpStack();
@@ -92,6 +94,9 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
IpcClient::Interface()->clearSavedRoutes();
}
} else if (state == Vpn::ConnectionState::Connecting) {
} else if (state == Vpn::ConnectionState::Disconnected) {
}
}
#endif
@@ -118,10 +123,10 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode)
QStringList sites;
const QVariantMap &m = m_settings->vpnSites(mode);
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
if (Utils::checkIpSubnetFormat(i.key())) {
if (NetworkUtilities::checkIpSubnetFormat(i.key())) {
ips.append(i.key());
} else {
if (Utils::checkIpSubnetFormat(i.value().toString())) {
if (NetworkUtilities::checkIpSubnetFormat(i.value().toString())) {
ips.append(i.value().toString());
}
sites.append(i.key());
@@ -246,8 +251,7 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser
configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData);
if (serverIndex >= 0) {
qDebug() << "VpnConnection::createVpnConfiguration: saving config for server #" << serverIndex << container
<< proto;
qDebug() << "VpnConnection::createVpnConfiguration: saving config for server #" << serverIndex << container;
QJsonObject protoObject = m_settings->protocolConfig(serverIndex, container, proto);
protoObject.insert(config_key::last_config, configDataBeforeLocalProcessing);
m_settings->setProtocolConfig(serverIndex, container, proto, protoObject);
@@ -461,6 +465,7 @@ QString VpnConnection::bytesPerSecToText(quint64 bytes)
void VpnConnection::disconnectFromVpn()
{
#ifdef AMNEZIA_DESKTOP
QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
if (IpcClient::Interface()) {
IpcClient::Interface()->flushDns();
+3
View File
@@ -13,6 +13,7 @@ namespace amnezia {
enum PermittedProcess {
OpenVPN,
Wireguard,
Tun2Socks,
CertUtil
};
@@ -24,6 +25,8 @@ inline QString permittedProcessPath(PermittedProcess pid)
return Utils::wireguardExecPath();
} else if (pid == PermittedProcess::CertUtil) {
return Utils::certUtilPath();
} else if (pid == PermittedProcess::Tun2Socks) {
return Utils::tun2socksPath();
}
return "";
}
+8
View File
@@ -1,6 +1,7 @@
#include <QtCore>
#include <QString>
#include <QJsonObject>
#include <QHostAddress>
#include "../client/daemon/interfaceconfig.h"
class IpcInterface
@@ -21,8 +22,15 @@ class IpcInterface
SLOT( void cleanUp() );
SLOT( void setLogsEnabled(bool enabled) );
SLOT( bool createTun(const QString &dev, const QString &subnet) );
SLOT( bool deleteTun(const QString &dev) );
SLOT( void StartRoutingIpv6() );
SLOT( void StopRoutingIpv6() );
SLOT( bool disableKillSwitch() );
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
};
+25
View File
@@ -160,6 +160,30 @@ void IpcServer::cleanUp()
Logger::cleanUp();
}
bool IpcServer::createTun(const QString &dev, const QString &subnet)
{
return Router::createTun(dev, subnet);
}
bool IpcServer::deleteTun(const QString &dev)
{
return Router::deleteTun(dev);
}
bool IpcServer::updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers)
{
return Router::updateResolvers(ifname, resolvers);
}
void IpcServer::StartRoutingIpv6()
{
Router::StartRoutingIpv6();
}
void IpcServer::StopRoutingIpv6()
{
Router::StopRoutingIpv6();
}
void IpcServer::setLogsEnabled(bool enabled)
{
#ifdef MZ_DEBUG
@@ -223,6 +247,7 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
+5
View File
@@ -27,9 +27,14 @@ public:
virtual QStringList getTapList() override;
virtual void cleanUp() override;
virtual void setLogsEnabled(bool enabled) override;
virtual bool createTun(const QString &dev, const QString &subnet) override;
virtual bool deleteTun(const QString &dev) override;
virtual void StartRoutingIpv6() override;
virtual void StopRoutingIpv6() override;
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
virtual bool disableKillSwitch() override;
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
private:
int m_localpid = 0;
+2
View File
@@ -32,6 +32,8 @@ void IpcServerProcess::start()
if (m_process->program().isEmpty()) {
qDebug() << "IpcServerProcess failed to start, program is empty";
}
Utils::killProcessByName(m_process->program());
m_process->start();
qDebug() << "IpcServerProcess started, " << m_process->program() << m_process->arguments();
+2
View File
@@ -14,6 +14,7 @@ configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/vers
set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.h
@@ -26,6 +27,7 @@ set(HEADERS
set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp
${CMAKE_CURRENT_LIST_DIR}/localserver.cpp
+58
View File
@@ -64,3 +64,61 @@ void Router::resetIpStack()
#endif
}
bool Router::createTun(const QString &dev, const QString &subnet)
{
#ifdef Q_OS_LINUX
return RouterLinux::Instance().createTun(dev, subnet);
#endif
#ifdef Q_OS_MAC
return RouterMac::Instance().createTun(dev, subnet);
#endif
return true;
};
bool Router::deleteTun(const QString &dev)
{
#ifdef Q_OS_LINUX
return RouterLinux::Instance().deleteTun(dev);
#endif
#ifdef Q_OS_MAC
return RouterMac::Instance().deleteTun(dev);
#endif
return true;
};
bool Router::updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers)
{
#ifdef Q_OS_LINUX
return RouterLinux::Instance().updateResolvers(ifname, resolvers);
#endif
#ifdef Q_OS_MACOS
return RouterMac::Instance().updateResolvers(ifname, resolvers);
#endif
#ifdef Q_OS_WIN
return RouterWin::Instance().updateResolvers(ifname, resolvers);
#endif
}
void Router::StopRoutingIpv6()
{
#ifdef Q_OS_WIN
RouterWin::Instance().StopRoutingIpv6();
#elif defined (Q_OS_MAC)
// todo fixme
#elif defined Q_OS_LINUX
RouterLinux::Instance().StopRoutingIpv6();
#endif
}
void Router::StartRoutingIpv6()
{
#ifdef Q_OS_WIN
RouterWin::Instance().StartRoutingIpv6();
#elif defined (Q_OS_MAC)
// todo fixme
#elif defined Q_OS_LINUX
RouterLinux::Instance().StartRoutingIpv6();
#endif
}
+6
View File
@@ -7,6 +7,7 @@
#include <QHash>
#include <QDebug>
#include <QObject>
#include <QHostAddress>
/**
* @brief The Router class - General class for handling ip routing
@@ -20,6 +21,11 @@ public:
static int routeDeleteList(const QString &gw, const QStringList &ips);
static void flushDns();
static void resetIpStack();
static bool createTun(const QString &dev, const QString &subnet);
static bool deleteTun(const QString &dev);
static void StartRoutingIpv6();
static void StopRoutingIpv6();
static bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
};
#endif // ROUTER_H
+181 -9
View File
@@ -5,10 +5,14 @@
#include <utilities.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/route.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <paths.h>
#include <fcntl.h>
#include <errno.h>
@@ -16,6 +20,8 @@
#include <unistd.h>
#include <QFileInfo>
#include <core/networkUtilities.h>
RouterLinux &RouterLinux::Instance()
{
static RouterLinux s;
@@ -24,10 +30,10 @@ RouterLinux &RouterLinux::Instance()
bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const int &sock)
{
QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = Utils::netMaskFromIpWithSubnet(ipWithSubnet);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipWithSubnet);
if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) {
if (!NetworkUtilities::checkIPv4Format(ip) || !NetworkUtilities::checkIPv4Format(gw)) {
qCritical().noquote() << "Critical, trying to add invalid route: " << ip << gw;
return false;
}
@@ -91,18 +97,18 @@ bool RouterLinux::clearSavedRoutes()
bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, const int &sock)
{
#ifdef MZ_DEBUG
qDebug().noquote() << "RouterMac::routeDelete: " << ipWithSubnet << gw;
qDebug().noquote() << "RouterLinux::routeDelete: " << ipWithSubnet << gw;
#endif
QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = Utils::netMaskFromIpWithSubnet(ipWithSubnet);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipWithSubnet);
if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) {
if (!NetworkUtilities::checkIPv4Format(ip) || !NetworkUtilities::checkIPv4Format(gw)) {
qCritical().noquote() << "Critical, trying to remove invalid route: " << ip << gw;
return false;
}
if (ip == "0.0.0.0") {
if (ipWithSubnet == "0.0.0.0/0") {
qDebug().noquote() << "Warning, trying to remove default route, skipping: " << ip << gw;
return true;
}
@@ -170,3 +176,169 @@ void RouterLinux::flushDns()
else
qDebug().noquote() << "OUTPUT systemctl restart nscd/systemd-resolved: " + output;
}
bool RouterLinux::createTun(const QString &dev, const QString &subnet) {
qDebug().noquote() << "createTun start";
QProcess process;
QStringList commands;
commands << "ip" << "tuntap" << "add" << "mode" << "tun" << "dev" << dev;
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start adding tun device!\n";
return false;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not add tun device!\n";
return false;
}
commands.clear();
commands << "ip" << "addr" << "add" << QString("%1/24").arg(subnet) << "dev" << dev;
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start adding a subnet for tun device!\n";
return false;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not add a subnet for tun device!\n";
return false;
}
commands.clear();
commands << "ip" << "link" << "set" << "dev" << dev << "up";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start link set for tun device!\n";
return false;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not link set for tun device!\n";
return false;
}
return true;
}
bool RouterLinux::deleteTun(const QString &dev)
{
struct {
struct nlmsghdr nh;
struct ifinfomsg ifm;
unsigned char data[64];
} req;
struct rtattr *rta;
int ret, rtnl;
rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (rtnl < 0) {
qDebug().noquote() << "can't open rtnl: " << errno;
return 1;
}
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.ifm)));
req.nh.nlmsg_flags = NLM_F_REQUEST;
req.nh.nlmsg_type = RTM_DELLINK;
req.ifm.ifi_family = AF_UNSPEC;
rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len));
rta->rta_type = IFLA_IFNAME;
rta->rta_len = RTA_LENGTH(IFNAMSIZ);
req.nh.nlmsg_len += rta->rta_len;
memcpy(RTA_DATA(rta), dev.toStdString().c_str(), IFNAMSIZ);
ret = send(rtnl, &req, req.nh.nlmsg_len, 0);
if (ret < 0)
qDebug().noquote() << "can't send: errno";
ret = (unsigned int)ret != req.nh.nlmsg_len;
close(rtnl);
qDebug().noquote() << "deleteTun ret" << ret;
return ret;
}
bool RouterLinux::updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers)
{
return m_dnsUtil->updateResolvers(ifname, resolvers);
}
void RouterLinux::StartRoutingIpv6()
{
QProcess process;
QStringList commands;
commands << "sysctl" << "-w" << "net.ipv6.conf.all.disable_ipv6=0";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start activate ipv6\n";
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not activate ipv6\n";
return;
}
commands.clear();
commands << "sysctl" << "-w" << "net.ipv6.conf.default.disable_ipv6=0";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start activate ipv6\n";
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not activate ipv6\n";
return;
}
commands.clear();
qDebug().noquote() << "StartRoutingIpv6 OK";
}
void RouterLinux::StopRoutingIpv6()
{
QProcess process;
QStringList commands;
commands << "sysctl" << "-w" << "net.ipv6.conf.all.disable_ipv6=1";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start disable ipv6\n";
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not disable ipv6\n";
return;
}
commands.clear();
commands << "sysctl" << "-w" << "net.ipv6.conf.default.disable_ipv6=1";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start disable ipv6\n";
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not disable ipv6\n";
return;
}
commands.clear();
qDebug().noquote() << "StopRoutingIpv6 OK";
}
+9 -2
View File
@@ -8,6 +8,8 @@
#include <QDebug>
#include <QObject>
#include "../client/platforms/linux/daemon/dnsutilslinux.h"
/**
* @brief The Router class - General class for handling ip routing
*/
@@ -29,15 +31,20 @@ public:
bool routeDeleteList(const QString &gw, const QStringList &ips);
QString getgatewayandiface();
void flushDns();
bool createTun(const QString &dev, const QString &subnet);
bool deleteTun(const QString &dev);
void StartRoutingIpv6();
void StopRoutingIpv6();
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
public slots:
private:
RouterLinux() {}
RouterLinux() {m_dnsUtil = new DnsUtilsLinux(this);}
RouterLinux(RouterLinux const &) = delete;
RouterLinux& operator= (RouterLinux const&) = delete;
QList<Route> m_addedRoutes;
DnsUtilsLinux *m_dnsUtil;
};
#endif // ROUTERLINUX_H
+45 -8
View File
@@ -3,7 +3,8 @@
#include <QProcess>
#include <QThread>
#include <utilities.h>
#include <core/networkUtilities.h>
RouterMac &RouterMac::Instance()
{
@@ -13,14 +14,14 @@ RouterMac &RouterMac::Instance()
bool RouterMac::routeAdd(const QString &ipWithSubnet, const QString &gw)
{
QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = Utils::netMaskFromIpWithSubnet(ipWithSubnet);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipWithSubnet);
#ifdef MZ_DEBUG
qDebug().noquote() << "RouterMac::routeAdd: " << ipWithSubnet << gw;
#endif
if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) {
if (!NetworkUtilities::checkIPv4Format(ip) || !NetworkUtilities::checkIPv4Format(gw)) {
qCritical().noquote() << "Critical, trying to add invalid route: " << ip << gw;
return false;
}
@@ -76,19 +77,19 @@ bool RouterMac::clearSavedRoutes()
bool RouterMac::routeDelete(const QString &ipWithSubnet, const QString &gw)
{
QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = Utils::netMaskFromIpWithSubnet(ipWithSubnet);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipWithSubnet);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipWithSubnet);
#ifdef MZ_DEBUG
qDebug().noquote() << "RouterMac::routeDelete: " << ipWithSubnet << gw;
#endif
if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) {
if (!NetworkUtilities::checkIPv4Format(ip) || !NetworkUtilities::checkIPv4Format(gw)) {
qCritical().noquote() << "Critical, trying to remove invalid route: " << ip << gw;
return false;
}
if (ip == "0.0.0.0") {
if (ipWithSubnet == "0.0.0.0/0") {
qDebug().noquote() << "Warning, trying to remove default route, skipping: " << ip << gw;
return true;
}
@@ -129,6 +130,42 @@ bool RouterMac::routeDeleteList(const QString &gw, const QStringList &ips)
return cnt;
}
bool RouterMac::createTun(const QString &dev, const QString &subnet) {
qDebug().noquote() << "createTun start";
QProcess process;
QStringList commands;
commands << "ifconfig" << dev << "inet" << subnet << subnet << "up";
process.start("sudo", commands);
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start activate tun device!\n";
return false;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not activate tun device!\n";
return false;
}
commands.clear();
return true;
}
bool RouterMac::updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers)
{
return m_dnsUtil->updateResolvers(ifname, resolvers);
}
bool RouterMac::deleteTun(const QString &dev)
{
qDebug().noquote() << "deleteTun start";
return true;
}
void RouterMac::flushDns()
{
// sudo killall -HUP mDNSResponder
+7 -2
View File
@@ -8,6 +8,7 @@
#include <QDebug>
#include <QObject>
#include "../client/platforms/macos/daemon/dnsutilsmacos.h"
/**
* @brief The Router class - General class for handling ip routing
@@ -29,15 +30,19 @@ public:
bool routeDelete(const QString &ip, const QString &gw);
bool routeDeleteList(const QString &gw, const QStringList &ips);
void flushDns();
bool createTun(const QString &dev, const QString &subnet);
bool deleteTun(const QString &dev);
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
public slots:
private:
RouterMac() {}
RouterMac() {m_dnsUtil = new DnsUtilsMacos(this);}
RouterMac(RouterMac const &) = delete;
RouterMac& operator= (RouterMac const&) = delete;
QList<Route> m_addedRoutes;
DnsUtilsMacos *m_dnsUtil;
};
#endif // ROUTERMAC_H
+59 -14
View File
@@ -1,5 +1,4 @@
#include "router_win.h"
#include "../client/utilities.h"
#include <string>
#include <tlhelp32.h>
@@ -7,6 +6,8 @@
#include <QProcess>
#include <core/networkUtilities.h>
LONG (NTAPI * NtSuspendProcess)(HANDLE ProcessHandle) = NULL;
LONG (NTAPI * NtResumeProcess)(HANDLE ProcessHandle) = NULL;
@@ -35,7 +36,7 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
// .arg(ips.join("\n"));
if (!Utils::checkIPv4Format(gw)) {
if (!NetworkUtilities::checkIPv4Format(gw)) {
qCritical().noquote() << "Trying to add invalid route, gw: " << gw;
return 0;
}
@@ -58,7 +59,6 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
@@ -66,7 +66,6 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
return 0;
}
int success_count = 0;
MIB_IPFORWARDROW ipfrow;
@@ -77,7 +76,6 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
ipfrow.dwForwardType = MIB_IPROUTE_TYPE_INDIRECT; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = MIB_IPPROTO_NETMGMT; /* XXX - MIB_PROTO_NETMGMT */
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
@@ -105,14 +103,14 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
for (int i = 0; i < ips.size(); ++i) {
QString ipWithMask = ips.at(i);
QString ip = Utils::ipAddressFromIpWithSubnet(ipWithMask);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipWithMask);
if (!Utils::checkIPv4Format(ip)) {
if (!NetworkUtilities::checkIPv4Format(ip)) {
qCritical().noquote() << "Critical, trying to add invalid route, ip: " << ip;
continue;
}
QString mask = Utils::netMaskFromIpWithSubnet(ipWithMask);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipWithMask);
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
@@ -137,7 +135,6 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
}
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
@@ -215,8 +212,7 @@ int RouterWin::routeDeleteList(const QString &gw, const QStringList &ips)
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
ULONG gw_addr= inet_addr(gw.toStdString().c_str());
ULONG gw_addr = inet_addr(gw.toStdString().c_str());
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
@@ -230,7 +226,6 @@ int RouterWin::routeDeleteList(const QString &gw, const QStringList &ips)
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
@@ -244,8 +239,8 @@ int RouterWin::routeDeleteList(const QString &gw, const QStringList &ips)
for (int i = 0; i < ips.size(); ++i) {
QString ipMask = ips.at(i);
if (ipMask.isEmpty()) continue;
QString ip = Utils::ipAddressFromIpWithSubnet(ipMask);
QString mask = Utils::netMaskFromIpWithSubnet(ipMask);
QString ip = NetworkUtilities::ipAddressFromIpWithSubnet(ipMask);
QString mask = NetworkUtilities::netMaskFromIpWithSubnet(ipMask);
if (ip.isEmpty()) continue;
@@ -443,3 +438,53 @@ BOOL RouterWin::SuspendProcess(BOOL fSuspend, DWORD dwProcessId)
return ok;
}
bool RouterWin::updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers)
{
return m_dnsUtil->updateResolvers(ifname, resolvers);
}
void RouterWin::StopRoutingIpv6()
{
{
QProcess p;
QString command = QString("interface ipv6 add route fc00::/7 interface={NetworkInterface.IPv6LoopbackInterfaceIndex} metric=0 store=active");
p.start(command);
p.waitForFinished();
}
{
QProcess p;
QString command = QString("interface ipv6 add route 2000::/4 interface={NetworkInterface.IPv6LoopbackInterfaceIndex} metric=0 store=active");
p.start(command);
p.waitForFinished();
}
{
QProcess p;
QString command = QString("interface ipv6 add route 3000::/4 interface={NetworkInterface.IPv6LoopbackInterfaceIndex} metric=0 store=active");
p.start(command);
p.waitForFinished();
}
}
void RouterWin::StartRoutingIpv6()
{
{
QProcess p;
QString command = QString("interface ipv6 delete route fc00::/7 interface={NetworkInterface.IPv6LoopbackInterfaceIndex}");
p.start(command);
p.waitForFinished();
}
{
QProcess p;
QString command = QString("interface ipv6 delete route 2000::/4 interface={NetworkInterface.IPv6LoopbackInterfaceIndex}");
p.start(command);
p.waitForFinished();
}
{
QProcess p;
QString command = QString("interface ipv6 delete route 3000::/4 interface={NetworkInterface.IPv6LoopbackInterfaceIndex}");
p.start(command);
p.waitForFinished();
}
}
+7 -3
View File
@@ -8,6 +8,7 @@
#include <QDebug>
#include <QObject>
#include "../client/platforms/windows/daemon/dnsutilswindows.h"
#include <WinSock2.h> //includes Windows.h
#include <WS2tcpip.h>
@@ -41,10 +42,13 @@ public:
void flushDns();
void resetIpStack();
void StartRoutingIpv6();
void StopRoutingIpv6();
void suspendWcmSvc(bool suspend);
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
private:
RouterWin() {}
RouterWin(RouterWin const &) = delete;
RouterWin& operator= (RouterWin const&) = delete;
@@ -54,11 +58,11 @@ private:
BOOL InitNtFunctions();
BOOL SuspendProcess(BOOL fSuspend, DWORD dwProcessId);
private:
RouterWin() {m_dnsUtil = new DnsUtilsWindows(this);}
QMultiMap<QString, MIB_IPFORWARDROW> m_ipForwardRows;
bool m_suspended = false;
DnsUtilsWindows *m_dnsUtil;
};
#endif // ROUTERWIN_H