Compare commits
29 Commits
feature/co
...
fix/setup_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
407350a4b4 | ||
|
|
c98faf3603 | ||
|
|
ea6618b2f6 | ||
|
|
7b092e73ad | ||
|
|
b2e25c42c7 | ||
|
|
c8dd38ac31 | ||
|
|
563ee4703f | ||
|
|
beceed81de | ||
|
|
3bf96253db | ||
|
|
da2d0ec203 | ||
|
|
008b858203 | ||
|
|
130fc8277d | ||
|
|
468d3357b8 | ||
|
|
f1271da527 | ||
|
|
249a7c7ca3 | ||
|
|
0094d0ebc4 | ||
|
|
834b504dff | ||
|
|
a516d0e757 | ||
|
|
afdfbdbc59 | ||
|
|
ef712b7054 | ||
|
|
c22f9ff08a | ||
|
|
04fb1825d5 | ||
|
|
4f8f873682 | ||
|
|
9fe75c6120 | ||
|
|
bb7e8f46cb | ||
|
|
5db0c281ee | ||
|
|
aac9bfcea6 | ||
|
|
e6ee9085a2 | ||
|
|
d62ade58a5 |
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.5.3.0
|
||||
project(${PROJECT} VERSION 4.6.0.1
|
||||
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 52)
|
||||
set(APP_ANDROID_VERSION_CODE 54)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -9,7 +9,7 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
|
||||
## Features
|
||||
|
||||
- Very easy to use - enter your IP address, SSH login, and password, and Amnezia will automatically install VPN docker containers to your server and connect to the VPN.
|
||||
- OpenVPN, ShadowSocks, WireGuard, and IKEv2 protocols support.
|
||||
- OpenVPN, Shadowsocks, WireGuard, and IKEv2 protocols support.
|
||||
- Masking VPN with OpenVPN over Cloak plugin
|
||||
- Split tunneling support - add any sites to the client to enable VPN only for them (only for desktops)
|
||||
- Windows, MacOS, Linux, Android, iOS releases.
|
||||
@@ -27,7 +27,7 @@ AmneziaVPN uses several open-source projects to work:
|
||||
|
||||
- [OpenSSL](https://www.openssl.org/)
|
||||
- [OpenVPN](https://openvpn.net/)
|
||||
- [ShadowSocks](https://shadowsocks.org/)
|
||||
- [Shadowsocks](https://shadowsocks.org/)
|
||||
- [Qt](https://www.qt.io/)
|
||||
- [LibSsh](https://libssh.org) - forked from Qt Creator
|
||||
- and more...
|
||||
|
||||
@@ -351,6 +351,9 @@ void AmneziaApplication::initModels()
|
||||
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
||||
|
||||
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
|
||||
|
||||
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
|
||||
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "ui/models/protocols_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
#include "ui/models/clientManagementModel.h"
|
||||
#include "ui/models/appSplitTunnelingModel.h"
|
||||
@@ -114,6 +115,7 @@ private:
|
||||
#endif
|
||||
|
||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QThread m_vpnConnectionThread;
|
||||
|
||||
@@ -136,8 +136,34 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".AmneziaVpnService"
|
||||
android:process=":amneziaVpnService"
|
||||
android:name=".AwgService"
|
||||
android:process=":amneziaAwgService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="systemExempted"
|
||||
android:exported="false"
|
||||
tools:ignore="ForegroundServicePermission">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".OpenVpnService"
|
||||
android:process=":amneziaOpenVpnService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="systemExempted"
|
||||
android:exported="false"
|
||||
tools:ignore="ForegroundServicePermission">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".XrayService"
|
||||
android:process=":amneziaXrayService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="systemExempted"
|
||||
android:exported="false"
|
||||
|
||||
@@ -3,6 +3,7 @@ import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
id("property-delegate")
|
||||
}
|
||||
|
||||
@@ -68,6 +69,12 @@ android {
|
||||
}
|
||||
signingConfig = signingConfigs["release"]
|
||||
}
|
||||
|
||||
create("fdroid") {
|
||||
initWith(getByName("release"))
|
||||
signingConfig = null
|
||||
matchingFallbacks += "release"
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
@@ -98,7 +105,6 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
implementation(project(":qt"))
|
||||
implementation(project(":utils"))
|
||||
implementation(project(":protocolApi"))
|
||||
@@ -106,9 +112,11 @@ dependencies {
|
||||
implementation(project(":awg"))
|
||||
implementation(project(":openvpn"))
|
||||
implementation(project(":cloak"))
|
||||
implementation(project(":xray"))
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
implementation(libs.kotlinx.serialization.protobuf)
|
||||
implementation(libs.bundles.androidx.camera)
|
||||
implementation(libs.google.mlkit)
|
||||
implementation(libs.androidx.datastore)
|
||||
|
||||
@@ -8,6 +8,7 @@ androidx-camera = "1.3.0"
|
||||
androidx-security-crypto = "1.1.0-alpha06"
|
||||
androidx-datastore = "1.1.0-beta01"
|
||||
kotlinx-coroutines = "1.7.3"
|
||||
kotlinx-serialization = "1.6.3"
|
||||
google-mlkit = "17.2.0"
|
||||
|
||||
[libraries]
|
||||
@@ -21,6 +22,7 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
|
||||
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
|
||||
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
|
||||
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinx-serialization" }
|
||||
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
|
||||
|
||||
[bundles]
|
||||
@@ -35,3 +37,4 @@ androidx-camera = [
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package org.amnezia.vpn.protocol.openvpn
|
||||
|
||||
import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import net.openvpn.ovpn3.ClientAPI_Config
|
||||
import org.amnezia.vpn.protocol.BadConfigException
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.Statistics
|
||||
import org.amnezia.vpn.protocol.VpnStartException
|
||||
@@ -37,7 +35,6 @@ import org.json.JSONObject
|
||||
|
||||
open class OpenVpn : Protocol() {
|
||||
|
||||
private lateinit var context: Context
|
||||
private var openVpnClient: OpenVpnClient? = null
|
||||
private lateinit var scope: CoroutineScope
|
||||
|
||||
@@ -53,10 +50,11 @@ open class OpenVpn : Protocol() {
|
||||
return Statistics.EMPTY_STATISTICS
|
||||
}
|
||||
|
||||
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
super.initialize(context, state, onError)
|
||||
loadSharedLibrary(context, "ovpn3")
|
||||
this.context = context
|
||||
override fun internalInit() {
|
||||
if (!isInitialized) loadSharedLibrary(context, "ovpn3")
|
||||
if (this::scope.isInitialized) {
|
||||
scope.cancel()
|
||||
}
|
||||
scope = CoroutineScope(Dispatchers.IO)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,21 @@ private const val SPLIT_TUNNEL_EXCLUDE = 2
|
||||
abstract class Protocol {
|
||||
|
||||
abstract val statistics: Statistics
|
||||
protected lateinit var context: Context
|
||||
protected lateinit var state: MutableStateFlow<ProtocolState>
|
||||
protected lateinit var onError: (String) -> Unit
|
||||
protected var isInitialized: Boolean = false
|
||||
|
||||
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
this.context = context
|
||||
this.state = state
|
||||
this.onError = onError
|
||||
internalInit()
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
protected abstract fun internalInit()
|
||||
|
||||
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
|
||||
|
||||
abstract fun stopVpn()
|
||||
|
||||
@@ -21,5 +21,5 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 30 KiB |
@@ -36,6 +36,8 @@ include(":wireguard")
|
||||
include(":awg")
|
||||
include(":openvpn")
|
||||
include(":cloak")
|
||||
include(":xray")
|
||||
include(":xray:libXray")
|
||||
|
||||
// get values from gradle or local properties
|
||||
val androidBuildToolsVersion: String by gradleProperties
|
||||
|
||||
@@ -34,6 +34,7 @@ import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -43,6 +44,8 @@ import org.amnezia.vpn.protocol.getStatus
|
||||
import org.amnezia.vpn.qt.QtAndroidController
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.Prefs
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.qtproject.qt.android.bindings.QtActivity
|
||||
|
||||
private const val TAG = "AmneziaActivity"
|
||||
@@ -59,6 +62,7 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
private lateinit var mainScope: CoroutineScope
|
||||
private val qtInitialized = CompletableDeferred<Unit>()
|
||||
private var vpnProto: VpnProto? = null
|
||||
private var isWaitingStatus = true
|
||||
private var isServiceConnected = false
|
||||
private var isInBoundState = false
|
||||
@@ -141,6 +145,7 @@ class AmneziaActivity : QtActivity() {
|
||||
override fun onBindingDied(name: ComponentName?) {
|
||||
Log.w(TAG, "Binding to the ${name?.flattenToString()} unexpectedly died")
|
||||
doUnbindService()
|
||||
QtAndroidController.onServiceDisconnected()
|
||||
doBindService()
|
||||
}
|
||||
}
|
||||
@@ -153,15 +158,20 @@ class AmneziaActivity : QtActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.d(TAG, "Create Amnezia activity: $intent")
|
||||
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
val proto = mainScope.async(Dispatchers.IO) {
|
||||
VpnStateStore.getVpnState().vpnProto
|
||||
}
|
||||
vpnServiceMessenger = IpcMessenger(
|
||||
"VpnService",
|
||||
onDeadObjectException = {
|
||||
doUnbindService()
|
||||
QtAndroidController.onServiceDisconnected()
|
||||
doBindService()
|
||||
}
|
||||
)
|
||||
registerBroadcastReceivers()
|
||||
intent?.let(::processIntent)
|
||||
runBlocking { vpnProto = proto.await() }
|
||||
}
|
||||
|
||||
private fun registerBroadcastReceivers() {
|
||||
@@ -209,13 +219,18 @@ class AmneziaActivity : QtActivity() {
|
||||
Log.d(TAG, "Start Amnezia activity")
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
doBindService()
|
||||
vpnProto?.let { proto ->
|
||||
if (AmneziaVpnService.isRunning(applicationContext, proto.processName)) {
|
||||
doBindService()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
Log.d(TAG, "Stop Amnezia activity")
|
||||
doUnbindService()
|
||||
QtAndroidController.onServiceDisconnected()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
@@ -269,10 +284,12 @@ class AmneziaActivity : QtActivity() {
|
||||
@MainThread
|
||||
private fun doBindService() {
|
||||
Log.d(TAG, "Bind service")
|
||||
Intent(this, AmneziaVpnService::class.java).also {
|
||||
bindService(it, serviceConnection, BIND_ABOVE_CLIENT and BIND_AUTO_CREATE)
|
||||
vpnProto?.let { proto ->
|
||||
Intent(this, proto.serviceClass).also {
|
||||
bindService(it, serviceConnection, BIND_ABOVE_CLIENT and BIND_AUTO_CREATE)
|
||||
}
|
||||
isInBoundState = true
|
||||
}
|
||||
isInBoundState = true
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@@ -280,7 +297,6 @@ class AmneziaActivity : QtActivity() {
|
||||
if (isInBoundState) {
|
||||
Log.d(TAG, "Unbind service")
|
||||
isWaitingStatus = true
|
||||
QtAndroidController.onServiceDisconnected()
|
||||
isServiceConnected = false
|
||||
vpnServiceMessenger.send(Action.UNREGISTER_CLIENT, activityMessenger)
|
||||
vpnServiceMessenger.reset()
|
||||
@@ -365,13 +381,31 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
@MainThread
|
||||
private fun startVpn(vpnConfig: String) {
|
||||
if (isServiceConnected) {
|
||||
connectToVpn(vpnConfig)
|
||||
} else {
|
||||
getVpnProto(vpnConfig)?.let { proto ->
|
||||
Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
|
||||
if (isServiceConnected) {
|
||||
if (proto == vpnProto) {
|
||||
connectToVpn(vpnConfig)
|
||||
return
|
||||
}
|
||||
doUnbindService()
|
||||
}
|
||||
vpnProto = proto
|
||||
isWaitingStatus = false
|
||||
startVpnService(vpnConfig)
|
||||
startVpnService(vpnConfig, proto)
|
||||
doBindService()
|
||||
}
|
||||
} ?: QtAndroidController.onServiceError()
|
||||
}
|
||||
|
||||
private fun getVpnProto(vpnConfig: String): VpnProto? = try {
|
||||
require(vpnConfig.isNotBlank()) { "Blank VPN config" }
|
||||
VpnProto.get(JSONObject(vpnConfig).getString("protocol"))
|
||||
} catch (e: JSONException) {
|
||||
Log.e(TAG, "Invalid VPN config json format: ${e.message}")
|
||||
null
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "Protocol not found: ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
private fun connectToVpn(vpnConfig: String) {
|
||||
@@ -383,15 +417,15 @@ class AmneziaActivity : QtActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun startVpnService(vpnConfig: String) {
|
||||
Log.d(TAG, "Start VPN service")
|
||||
Intent(this, AmneziaVpnService::class.java).apply {
|
||||
private fun startVpnService(vpnConfig: String, proto: VpnProto) {
|
||||
Log.d(TAG, "Start VPN service: $proto")
|
||||
Intent(this, proto.serviceClass).apply {
|
||||
putExtra(MSG_VPN_CONFIG, vpnConfig)
|
||||
}.also {
|
||||
try {
|
||||
ContextCompat.startForegroundService(this, it)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
|
||||
Log.e(TAG, "Failed to start ${proto.serviceClass.simpleName}: $e")
|
||||
QtAndroidController.onServiceError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ class AmneziaTileService : TileService() {
|
||||
|
||||
@Volatile
|
||||
private var isServiceConnected = false
|
||||
|
||||
@Volatile
|
||||
private var vpnProto: VpnProto? = null
|
||||
private var isInBoundState = false
|
||||
@Volatile
|
||||
private var isVpnConfigExists = false
|
||||
@@ -94,16 +97,21 @@ class AmneziaTileService : TileService() {
|
||||
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
Log.d(TAG, "Start listening")
|
||||
if (AmneziaVpnService.isRunning(applicationContext)) {
|
||||
Log.d(TAG, "Vpn service is running")
|
||||
doBindService()
|
||||
} else {
|
||||
Log.d(TAG, "Vpn service is not running")
|
||||
isServiceConnected = false
|
||||
updateVpnState(DISCONNECTED)
|
||||
scope.launch {
|
||||
Log.d(TAG, "Start listening")
|
||||
vpnProto = VpnStateStore.getVpnState().vpnProto
|
||||
vpnProto.also { proto ->
|
||||
if (proto != null && AmneziaVpnService.isRunning(applicationContext, proto.processName)) {
|
||||
Log.d(TAG, "Vpn service is running")
|
||||
doBindService()
|
||||
} else {
|
||||
Log.d(TAG, "Vpn service is not running")
|
||||
isServiceConnected = false
|
||||
updateVpnState(DISCONNECTED)
|
||||
}
|
||||
}
|
||||
vpnStateListeningJob = launchVpnStateListening()
|
||||
}
|
||||
vpnStateListeningJob = launchVpnStateListening()
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
@@ -124,7 +132,7 @@ class AmneziaTileService : TileService() {
|
||||
}
|
||||
|
||||
private fun onClickInternal() {
|
||||
if (isVpnConfigExists) {
|
||||
if (isVpnConfigExists && vpnProto != null) {
|
||||
Log.d(TAG, "Change VPN state")
|
||||
if (qsTile.state == Tile.STATE_INACTIVE) {
|
||||
Log.d(TAG, "Start VPN")
|
||||
@@ -147,10 +155,12 @@ class AmneziaTileService : TileService() {
|
||||
|
||||
private fun doBindService() {
|
||||
Log.d(TAG, "Bind service")
|
||||
Intent(this, AmneziaVpnService::class.java).also {
|
||||
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
|
||||
vpnProto?.let { proto ->
|
||||
Intent(this, proto.serviceClass).also {
|
||||
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
|
||||
}
|
||||
isInBoundState = true
|
||||
}
|
||||
isInBoundState = true
|
||||
}
|
||||
|
||||
private fun doUnbindService() {
|
||||
@@ -180,6 +190,7 @@ class AmneziaTileService : TileService() {
|
||||
if (VpnService.prepare(applicationContext) != null) {
|
||||
Intent(this, VpnRequestActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
putExtra(EXTRA_PROTOCOL, vpnProto)
|
||||
}.also {
|
||||
startActivityAndCollapseCompat(it)
|
||||
}
|
||||
@@ -189,14 +200,16 @@ class AmneziaTileService : TileService() {
|
||||
}
|
||||
|
||||
private fun startVpnService() {
|
||||
try {
|
||||
ContextCompat.startForegroundService(
|
||||
applicationContext,
|
||||
Intent(this, AmneziaVpnService::class.java)
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
|
||||
}
|
||||
vpnProto?.let { proto ->
|
||||
try {
|
||||
ContextCompat.startForegroundService(
|
||||
applicationContext,
|
||||
Intent(this, proto.serviceClass)
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Failed to start ${proto.serviceClass.simpleName}: $e")
|
||||
}
|
||||
} ?: Log.e(TAG, "Failed to start vpn service: vpnProto is null")
|
||||
}
|
||||
|
||||
private fun connectToVpn() = vpnServiceMessenger.send(Action.CONNECT)
|
||||
@@ -220,11 +233,8 @@ class AmneziaTileService : TileService() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVpnState(state: ProtocolState) {
|
||||
scope.launch {
|
||||
VpnStateStore.store { it.copy(protocolState = state) }
|
||||
}
|
||||
}
|
||||
private fun updateVpnState(state: ProtocolState) =
|
||||
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
|
||||
|
||||
private fun launchVpnStateListening() =
|
||||
scope.launch { VpnStateStore.dataFlow().collectLatest(::updateTile) }
|
||||
@@ -232,9 +242,10 @@ class AmneziaTileService : TileService() {
|
||||
private fun updateTile(vpnState: VpnState) {
|
||||
Log.d(TAG, "Update tile: $vpnState")
|
||||
isVpnConfigExists = vpnState.serverName != null
|
||||
vpnProto = vpnState.vpnProto
|
||||
val tile = qsTile ?: return
|
||||
tile.apply {
|
||||
label = vpnState.serverName ?: DEFAULT_TILE_LABEL
|
||||
label = (vpnState.serverName ?: DEFAULT_TILE_LABEL) + (vpnProto?.let { " ${it.label}" } ?: "")
|
||||
when (val protocolState = vpnState.protocolState) {
|
||||
CONNECTED -> {
|
||||
state = Tile.STATE_ACTIVE
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.ActivityManager
|
||||
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
|
||||
import android.app.NotificationManager
|
||||
@@ -39,7 +40,6 @@ import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.amnezia.vpn.protocol.BadConfigException
|
||||
import org.amnezia.vpn.protocol.LoadLibraryException
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
@@ -48,11 +48,7 @@ import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
|
||||
import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
|
||||
import org.amnezia.vpn.protocol.VpnException
|
||||
import org.amnezia.vpn.protocol.VpnStartException
|
||||
import org.amnezia.vpn.protocol.awg.Awg
|
||||
import org.amnezia.vpn.protocol.cloak.Cloak
|
||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||
import org.amnezia.vpn.protocol.putStatus
|
||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.Prefs
|
||||
import org.amnezia.vpn.util.net.NetworkState
|
||||
@@ -63,6 +59,7 @@ import org.json.JSONObject
|
||||
private const val TAG = "AmneziaVpnService"
|
||||
|
||||
const val ACTION_DISCONNECT = "org.amnezia.vpn.action.disconnect"
|
||||
const val ACTION_CONNECT = "org.amnezia.vpn.action.connect"
|
||||
|
||||
const val MSG_VPN_CONFIG = "VPN_CONFIG"
|
||||
const val MSG_ERROR = "ERROR"
|
||||
@@ -73,19 +70,18 @@ const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK"
|
||||
private const val PREFS_CONFIG_KEY = "LAST_CONF"
|
||||
private const val PREFS_SERVER_NAME = "LAST_SERVER_NAME"
|
||||
private const val PREFS_SERVER_INDEX = "LAST_SERVER_INDEX"
|
||||
private const val PROCESS_NAME = "org.amnezia.vpn:amneziaVpnService"
|
||||
// private const val STATISTICS_SENDING_TIMEOUT = 1000L
|
||||
private const val TRAFFIC_STATS_UPDATE_TIMEOUT = 1000L
|
||||
private const val DISCONNECT_TIMEOUT = 5000L
|
||||
private const val STOP_SERVICE_TIMEOUT = 5000L
|
||||
|
||||
class AmneziaVpnService : VpnService() {
|
||||
@SuppressLint("Registered")
|
||||
open class AmneziaVpnService : VpnService() {
|
||||
|
||||
private lateinit var mainScope: CoroutineScope
|
||||
private lateinit var connectionScope: CoroutineScope
|
||||
private var isServiceBound = false
|
||||
private var protocol: Protocol? = null
|
||||
private val protocolCache = mutableMapOf<String, Protocol>()
|
||||
private var vpnProto: VpnProto? = null
|
||||
private var protocolState = MutableStateFlow(UNKNOWN)
|
||||
private var serverName: String? = null
|
||||
private var serverIndex: Int = -1
|
||||
@@ -105,7 +101,7 @@ class AmneziaVpnService : VpnService() {
|
||||
// private var statisticsSendingJob: Job? = null
|
||||
private lateinit var networkState: NetworkState
|
||||
private lateinit var trafficStats: TrafficStats
|
||||
private var disconnectReceiver: BroadcastReceiver? = null
|
||||
private var controlReceiver: BroadcastReceiver? = null
|
||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
||||
private var screenOnReceiver: BroadcastReceiver? = null
|
||||
private var screenOffReceiver: BroadcastReceiver? = null
|
||||
@@ -116,7 +112,6 @@ class AmneziaVpnService : VpnService() {
|
||||
|
||||
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
|
||||
protocolState.value = DISCONNECTED
|
||||
protocol = null
|
||||
when (e) {
|
||||
is IllegalArgumentException,
|
||||
is VpnStartException,
|
||||
@@ -227,7 +222,8 @@ class AmneziaVpnService : VpnService() {
|
||||
connect(intent?.getStringExtra(MSG_VPN_CONFIG))
|
||||
}
|
||||
ServiceCompat.startForeground(
|
||||
this, NOTIFICATION_ID, serviceNotification.buildNotification(serverName, protocolState.value),
|
||||
this, NOTIFICATION_ID,
|
||||
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
|
||||
foregroundServiceTypeCompat
|
||||
)
|
||||
return START_REDELIVER_INTENT
|
||||
@@ -292,9 +288,17 @@ class AmneziaVpnService : VpnService() {
|
||||
|
||||
private fun registerBroadcastReceivers() {
|
||||
Log.d(TAG, "Register broadcast receivers")
|
||||
disconnectReceiver = registerBroadcastReceiver(ACTION_DISCONNECT, ContextCompat.RECEIVER_NOT_EXPORTED) {
|
||||
Log.d(TAG, "Broadcast request received: $ACTION_DISCONNECT")
|
||||
disconnect()
|
||||
controlReceiver = registerBroadcastReceiver(
|
||||
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
) {
|
||||
it?.action?.let { action ->
|
||||
Log.d(TAG, "Broadcast request received: $action")
|
||||
when (action) {
|
||||
ACTION_CONNECT -> connect()
|
||||
ACTION_DISCONNECT -> disconnect()
|
||||
else -> Log.w(TAG, "Unknown action received: $action")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
@@ -340,10 +344,10 @@ class AmneziaVpnService : VpnService() {
|
||||
|
||||
private fun unregisterBroadcastReceivers() {
|
||||
Log.d(TAG, "Unregister broadcast receivers")
|
||||
unregisterBroadcastReceiver(disconnectReceiver)
|
||||
unregisterBroadcastReceiver(controlReceiver)
|
||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
||||
unregisterScreenStateBroadcastReceivers()
|
||||
disconnectReceiver = null
|
||||
controlReceiver = null
|
||||
notificationStateReceiver = null
|
||||
}
|
||||
|
||||
@@ -356,7 +360,7 @@ class AmneziaVpnService : VpnService() {
|
||||
protocolState.drop(1).collect { protocolState ->
|
||||
Log.d(TAG, "Protocol state changed: $protocolState")
|
||||
|
||||
serviceNotification.updateNotification(serverName, protocolState)
|
||||
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState)
|
||||
|
||||
clientMessengers.send {
|
||||
ServiceEvent.STATUS_CHANGED.packToMessage {
|
||||
@@ -364,7 +368,7 @@ class AmneziaVpnService : VpnService() {
|
||||
}
|
||||
}
|
||||
|
||||
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex) }
|
||||
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex, vpnProto) }
|
||||
|
||||
when (protocolState) {
|
||||
CONNECTED -> {
|
||||
@@ -421,7 +425,7 @@ class AmneziaVpnService : VpnService() {
|
||||
@MainThread
|
||||
private fun enableNotification() {
|
||||
registerScreenStateBroadcastReceivers()
|
||||
serviceNotification.updateNotification(serverName, protocolState.value)
|
||||
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState.value)
|
||||
launchTrafficStatsUpdate()
|
||||
}
|
||||
|
||||
@@ -484,8 +488,6 @@ class AmneziaVpnService : VpnService() {
|
||||
|
||||
Log.d(TAG, "Start VPN connection")
|
||||
|
||||
protocolState.value = CONNECTING
|
||||
|
||||
val config = parseConfigToJson(vpnConfig)
|
||||
saveServerData(config)
|
||||
if (config == null) {
|
||||
@@ -494,6 +496,16 @@ class AmneziaVpnService : VpnService() {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
vpnProto = VpnProto.get(config.getString("protocol"))
|
||||
} catch (e: Exception) {
|
||||
onError("Invalid VPN config: ${e.message}")
|
||||
protocolState.value = DISCONNECTED
|
||||
return
|
||||
}
|
||||
|
||||
protocolState.value = CONNECTING
|
||||
|
||||
if (!checkPermission()) {
|
||||
protocolState.value = DISCONNECTED
|
||||
return
|
||||
@@ -503,8 +515,10 @@ class AmneziaVpnService : VpnService() {
|
||||
disconnectionJob?.join()
|
||||
disconnectionJob = null
|
||||
|
||||
protocol = getProtocol(config.getString("protocol"))
|
||||
protocol?.startVpn(config, Builder(), ::protect)
|
||||
vpnProto?.protocol?.let { protocol ->
|
||||
protocol.initialize(applicationContext, protocolState, ::onError)
|
||||
protocol.startVpn(config, Builder(), ::protect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,8 +534,8 @@ class AmneziaVpnService : VpnService() {
|
||||
connectionJob?.join()
|
||||
connectionJob = null
|
||||
|
||||
protocol?.stopVpn()
|
||||
protocol = null
|
||||
vpnProto?.protocol?.stopVpn()
|
||||
|
||||
try {
|
||||
withTimeout(DISCONNECT_TIMEOUT) {
|
||||
// waiting for disconnect state
|
||||
@@ -543,22 +557,10 @@ class AmneziaVpnService : VpnService() {
|
||||
protocolState.value = RECONNECTING
|
||||
|
||||
connectionJob = connectionScope.launch {
|
||||
protocol?.reconnectVpn(Builder())
|
||||
vpnProto?.protocol?.reconnectVpn(Builder())
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun getProtocol(protocolName: String): Protocol =
|
||||
protocolCache[protocolName]
|
||||
?: when (protocolName) {
|
||||
"wireguard" -> Wireguard()
|
||||
"awg" -> Awg()
|
||||
"openvpn" -> OpenVpn()
|
||||
"cloak" -> Cloak()
|
||||
else -> throw IllegalArgumentException("Protocol '$protocolName' not found")
|
||||
}.apply { initialize(applicationContext, protocolState, ::onError) }
|
||||
.also { protocolCache[protocolName] = it }
|
||||
|
||||
/**
|
||||
* Utils methods
|
||||
*/
|
||||
@@ -603,6 +605,7 @@ class AmneziaVpnService : VpnService() {
|
||||
if (prepare(applicationContext) != null) {
|
||||
Intent(this, VpnRequestActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
putExtra(EXTRA_PROTOCOL, vpnProto)
|
||||
}.also {
|
||||
startActivity(it)
|
||||
}
|
||||
@@ -612,9 +615,9 @@ class AmneziaVpnService : VpnService() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isRunning(context: Context): Boolean =
|
||||
fun isRunning(context: Context, processName: String): Boolean =
|
||||
context.getSystemService<ActivityManager>()!!.runningAppProcesses.any {
|
||||
it.processName == PROCESS_NAME && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
|
||||
it.processName == processName && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
client/android/src/org/amnezia/vpn/AwgService.kt
Normal file
@@ -0,0 +1,3 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
class AwgService : AmneziaVpnService()
|
||||
@@ -140,7 +140,7 @@ class CameraActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
Log.e(TAG, "Processing QR-code image failed: ${it.message}")
|
||||
Log.e(TAG, "Processing QR code image failed: ${it.message}")
|
||||
}.addOnCompleteListener {
|
||||
imageProxy.close()
|
||||
}
|
||||
|
||||
3
client/android/src/org/amnezia/vpn/OpenVpnService.kt
Normal file
@@ -0,0 +1,3 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
class OpenVpnService : AmneziaVpnService()
|
||||
@@ -59,14 +59,14 @@ class ServiceNotification(private val context: Context) {
|
||||
formatSpeedString(rxString, txString)
|
||||
}
|
||||
|
||||
fun buildNotification(serverName: String?, state: ProtocolState): Notification {
|
||||
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
|
||||
val speedString = if (state == CONNECTED) zeroSpeed else null
|
||||
|
||||
Log.d(TAG, "Build notification: $serverName, $state")
|
||||
|
||||
return notificationBuilder
|
||||
.setSmallIcon(R.drawable.ic_amnezia_round)
|
||||
.setContentTitle(serverName ?: "AmneziaVPN")
|
||||
.setContentTitle((serverName ?: "AmneziaVPN") + (protocol?.let { " $it" } ?: ""))
|
||||
.setContentText(context.getString(state))
|
||||
.setSubText(speedString)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
@@ -96,10 +96,10 @@ class ServiceNotification(private val context: Context) {
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun updateNotification(serverName: String?, state: ProtocolState) {
|
||||
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
||||
if (context.isNotificationPermissionGranted()) {
|
||||
Log.d(TAG, "Update notification: $serverName, $state")
|
||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, state))
|
||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class ServiceNotification(private val context: Context) {
|
||||
context,
|
||||
DISCONNECT_REQUEST_CODE,
|
||||
Intent(ACTION_DISCONNECT).apply {
|
||||
setPackage("org.amnezia.vpn")
|
||||
setPackage(context.packageName)
|
||||
},
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
@@ -135,10 +135,12 @@ class ServiceNotification(private val context: Context) {
|
||||
DISCONNECTED -> {
|
||||
Action(
|
||||
0, context.getString(R.string.connect),
|
||||
createServicePendingIntent(
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
CONNECT_REQUEST_CODE,
|
||||
Intent(context, AmneziaVpnService::class.java),
|
||||
Intent(ACTION_CONNECT).apply {
|
||||
setPackage(context.packageName)
|
||||
},
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
@@ -148,13 +150,6 @@ class ServiceNotification(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private val createServicePendingIntent: (Context, Int, Intent, Int) -> PendingIntent =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent::getForegroundService
|
||||
} else {
|
||||
PendingIntent::getService
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createNotificationChannel(context: Context) {
|
||||
with(NotificationManagerCompat.from(context)) {
|
||||
|
||||
67
client/android/src/org/amnezia/vpn/VpnProto.kt
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.awg.Awg
|
||||
import org.amnezia.vpn.protocol.cloak.Cloak
|
||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||
import org.amnezia.vpn.protocol.xray.Xray
|
||||
|
||||
enum class VpnProto(
|
||||
val label: String,
|
||||
val processName: String,
|
||||
val serviceClass: Class<out AmneziaVpnService>
|
||||
) {
|
||||
WIREGUARD(
|
||||
"WireGuard",
|
||||
"org.amnezia.vpn:amneziaAwgService",
|
||||
AwgService::class.java
|
||||
) {
|
||||
override fun createProtocol(): Protocol = Wireguard()
|
||||
},
|
||||
|
||||
AWG(
|
||||
"AmneziaWG",
|
||||
"org.amnezia.vpn:amneziaAwgService",
|
||||
AwgService::class.java
|
||||
) {
|
||||
override fun createProtocol(): Protocol = Awg()
|
||||
},
|
||||
|
||||
OPENVPN(
|
||||
"OpenVPN",
|
||||
"org.amnezia.vpn:amneziaOpenVpnService",
|
||||
OpenVpnService::class.java
|
||||
) {
|
||||
override fun createProtocol(): Protocol = OpenVpn()
|
||||
},
|
||||
|
||||
CLOAK(
|
||||
"Cloak",
|
||||
"org.amnezia.vpn:amneziaOpenVpnService",
|
||||
OpenVpnService::class.java
|
||||
) {
|
||||
override fun createProtocol(): Protocol = Cloak()
|
||||
},
|
||||
|
||||
XRAY(
|
||||
"XRay",
|
||||
"org.amnezia.vpn:amneziaXrayService",
|
||||
XrayService::class.java
|
||||
) {
|
||||
override fun createProtocol(): Protocol = Xray()
|
||||
};
|
||||
|
||||
private var _protocol: Protocol? = null
|
||||
val protocol: Protocol
|
||||
get() {
|
||||
if (_protocol == null) _protocol = createProtocol()
|
||||
return _protocol ?: throw AssertionError("Set to null by another thread")
|
||||
}
|
||||
|
||||
protected abstract fun createProtocol(): Protocol
|
||||
|
||||
companion object {
|
||||
fun get(protocolName: String): VpnProto = VpnProto.valueOf(protocolName.uppercase())
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import android.content.Intent
|
||||
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.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
@@ -18,9 +19,11 @@ import androidx.core.content.getSystemService
|
||||
import org.amnezia.vpn.util.Log
|
||||
|
||||
private const val TAG = "VpnRequestActivity"
|
||||
const val EXTRA_PROTOCOL = "PROTOCOL"
|
||||
|
||||
class VpnRequestActivity : ComponentActivity() {
|
||||
|
||||
private var vpnProto: VpnProto? = null
|
||||
private var userPresentReceiver: BroadcastReceiver? = null
|
||||
private val requestLauncher =
|
||||
registerForActivityResult(StartActivityForResult(), ::checkRequestResult)
|
||||
@@ -28,6 +31,12 @@ class VpnRequestActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.d(TAG, "Start request activity")
|
||||
vpnProto = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.extras?.getSerializable(EXTRA_PROTOCOL, VpnProto::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
|
||||
}
|
||||
val requestIntent = VpnService.prepare(applicationContext)
|
||||
if (requestIntent != null) {
|
||||
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
||||
@@ -66,10 +75,18 @@ class VpnRequestActivity : ComponentActivity() {
|
||||
|
||||
private fun onPermissionGranted() {
|
||||
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)
|
||||
vpnProto?.let { proto ->
|
||||
Intent(applicationContext, proto.serviceClass).apply {
|
||||
putExtra(AFTER_PERMISSION_CHECK, true)
|
||||
}.also {
|
||||
ContextCompat.startForegroundService(this, it)
|
||||
}
|
||||
} ?: run {
|
||||
Intent(this, AmneziaActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}.also {
|
||||
startActivity(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
import android.app.Application
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.MultiProcessDataStoreFactory
|
||||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
|
||||
import androidx.datastore.dataStoreFile
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.Serializable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.decodeFromByteArray
|
||||
import kotlinx.serialization.encodeToByteArray
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.util.Log
|
||||
@@ -21,13 +24,14 @@ import org.amnezia.vpn.util.Log
|
||||
private const val TAG = "VpnState"
|
||||
private const val STORE_FILE_NAME = "vpnState"
|
||||
|
||||
@Serializable
|
||||
data class VpnState(
|
||||
val protocolState: ProtocolState,
|
||||
val serverName: String? = null,
|
||||
val serverIndex: Int = -1
|
||||
) : Serializable {
|
||||
val serverIndex: Int = -1,
|
||||
val vpnProto: VpnProto? = null
|
||||
) {
|
||||
companion object {
|
||||
private const val serialVersionUID: Long = -1760654961004181606
|
||||
val defaultState: VpnState = VpnState(DISCONNECTED)
|
||||
}
|
||||
}
|
||||
@@ -37,7 +41,11 @@ object VpnStateStore {
|
||||
|
||||
private val dataStore = MultiProcessDataStoreFactory.create(
|
||||
serializer = VpnStateSerializer(),
|
||||
produceFile = { app.dataStoreFile(STORE_FILE_NAME) }
|
||||
produceFile = { app.dataStoreFile(STORE_FILE_NAME) },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler { e ->
|
||||
Log.e(TAG, "VpnState DataStore corrupted: $e")
|
||||
VpnState.defaultState
|
||||
}
|
||||
)
|
||||
|
||||
fun init(app: Application) {
|
||||
@@ -45,36 +53,36 @@ object VpnStateStore {
|
||||
this.app = app
|
||||
}
|
||||
|
||||
fun dataFlow(): Flow<VpnState> = dataStore.data
|
||||
fun dataFlow(): Flow<VpnState> = dataStore.data.catch { e ->
|
||||
Log.e(TAG, "Failed to read VpnState from store: ${e.message}")
|
||||
emit(VpnState.defaultState)
|
||||
}
|
||||
|
||||
suspend fun getVpnState(): VpnState = dataFlow().firstOrNull() ?: VpnState.defaultState
|
||||
|
||||
suspend fun store(f: (vpnState: VpnState) -> VpnState) {
|
||||
try {
|
||||
dataStore.updateData(f)
|
||||
} catch (e : Exception) {
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to store VpnState: $e")
|
||||
Log.w(TAG, "Remove DataStore file")
|
||||
app.dataStoreFile(STORE_FILE_NAME).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private class VpnStateSerializer : Serializer<VpnState> {
|
||||
override val defaultValue: VpnState = VpnState.defaultState
|
||||
|
||||
override suspend fun readFrom(input: InputStream): VpnState {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val bios = ByteArrayInputStream(input.readBytes())
|
||||
ObjectInputStream(bios).use {
|
||||
it.readObject() as VpnState
|
||||
}
|
||||
}
|
||||
override suspend fun readFrom(input: InputStream): VpnState = try {
|
||||
ProtoBuf.decodeFromByteArray<VpnState>(input.readBytes())
|
||||
} catch (e: SerializationException) {
|
||||
Log.e(TAG, "Failed to deserialize data: $e")
|
||||
throw CorruptionException("Failed to deserialize data", e)
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: VpnState, output: OutputStream) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val baos = ByteArrayOutputStream()
|
||||
ObjectOutputStream(baos).use {
|
||||
it.writeObject(t)
|
||||
}
|
||||
output.write(baos.toByteArray())
|
||||
}
|
||||
}
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun writeTo(t: VpnState, output: OutputStream) =
|
||||
output.write(ProtoBuf.encodeToByteArray(t))
|
||||
}
|
||||
|
||||
3
client/android/src/org/amnezia/vpn/XrayService.kt
Normal file
@@ -0,0 +1,3 @@
|
||||
package org.amnezia.vpn
|
||||
|
||||
class XrayService : AmneziaVpnService()
|
||||
@@ -1,12 +1,9 @@
|
||||
package org.amnezia.vpn.protocol.wireguard
|
||||
|
||||
import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import java.util.TreeMap
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.amnezia.awg.GoBackend
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.Statistics
|
||||
@@ -78,9 +75,8 @@ open class Wireguard : Protocol() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
super.initialize(context, state, onError)
|
||||
loadSharedLibrary(context, "wg-go")
|
||||
override fun internalInit() {
|
||||
if (!isInitialized) loadSharedLibrary(context, "wg-go")
|
||||
}
|
||||
|
||||
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
|
||||
19
client/android/xray/build.gradle.kts
Normal file
@@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id(libs.plugins.kotlin.android.get().pluginId)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn.protocol.xray"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":utils"))
|
||||
compileOnly(project(":protocolApi"))
|
||||
implementation(project(":xray:libXray"))
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
}
|
||||
6
client/android/xray/libXray/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
configurations {
|
||||
maybeCreate("default")
|
||||
}
|
||||
artifacts.add("default", file("libxray.aar"))
|
||||
237
client/android/xray/src/main/kotlin/Xray.kt
Normal file
@@ -0,0 +1,237 @@
|
||||
package org.amnezia.vpn.protocol.xray
|
||||
|
||||
import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import go.Seq
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.Statistics
|
||||
import org.amnezia.vpn.protocol.VpnStartException
|
||||
import org.amnezia.vpn.protocol.xray.libXray.DialerController
|
||||
import org.amnezia.vpn.protocol.xray.libXray.LibXray
|
||||
import org.amnezia.vpn.protocol.xray.libXray.Logger
|
||||
import org.amnezia.vpn.protocol.xray.libXray.Tun2SocksConfig
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.parseInetAddress
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Config example:
|
||||
* {
|
||||
* "appSplitTunnelType": 0,
|
||||
* "config_version": 0,
|
||||
* "description": "Server 1",
|
||||
* "dns1": "1.1.1.1",
|
||||
* "dns2": "1.0.0.1",
|
||||
* "hostName": "100.100.100.0",
|
||||
* "protocol": "xray",
|
||||
* "splitTunnelApps": [],
|
||||
* "splitTunnelSites": [],
|
||||
* "splitTunnelType": 0,
|
||||
* "xray_config_data": {
|
||||
* "inbounds": [
|
||||
* {
|
||||
* "listen": "127.0.0.1",
|
||||
* "port": 8080,
|
||||
* "protocol": "socks",
|
||||
* "settings": {
|
||||
* "udp": true
|
||||
* }
|
||||
* }
|
||||
* ],
|
||||
* "log": {
|
||||
* "loglevel": "error"
|
||||
* },
|
||||
* "outbounds": [
|
||||
* {
|
||||
* "protocol": "vless",
|
||||
* "settings": {
|
||||
* "vnext": [
|
||||
* {
|
||||
* "address": "100.100.100.0",
|
||||
* "port": 443,
|
||||
* "users": [
|
||||
* {
|
||||
* "encryption": "none",
|
||||
* "flow": "xtls-rprx-vision",
|
||||
* "id": "id"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* "streamSettings": {
|
||||
* "network": "tcp",
|
||||
* "realitySettings": {
|
||||
* "fingerprint": "chrome",
|
||||
* "publicKey": "publicKey",
|
||||
* "serverName": "google.com",
|
||||
* "shortId": "id",
|
||||
* "spiderX": ""
|
||||
* },
|
||||
* "security": "reality"
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
private const val TAG = "Xray"
|
||||
private const val LIBXRAY_TAG = "libXray"
|
||||
|
||||
class Xray : Protocol() {
|
||||
|
||||
private var isRunning: Boolean = false
|
||||
override val statistics: Statistics = Statistics.EMPTY_STATISTICS
|
||||
|
||||
override fun internalInit() {
|
||||
Seq.setContext(context)
|
||||
if (!isInitialized) {
|
||||
LibXray.initLogger(object : Logger {
|
||||
override fun warning(s: String) = Log.w(LIBXRAY_TAG, s)
|
||||
|
||||
override fun error(s: String) = Log.e(LIBXRAY_TAG, s)
|
||||
|
||||
override fun write(msg: ByteArray): Long {
|
||||
Log.w(LIBXRAY_TAG, String(msg))
|
||||
return msg.size.toLong()
|
||||
}
|
||||
}).isNotNullOrBlank { err ->
|
||||
Log.w(TAG, "Failed to initialize logger: $err")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
if (isRunning) {
|
||||
Log.w(TAG, "XRay already running")
|
||||
return
|
||||
}
|
||||
|
||||
val xrayJsonConfig = config.getJSONObject("xray_config_data")
|
||||
val xrayConfig = parseConfig(config, xrayJsonConfig)
|
||||
|
||||
// for debug
|
||||
// xrayJsonConfig.getJSONObject("log").put("loglevel", "debug")
|
||||
xrayJsonConfig.getJSONObject("log").put("loglevel", "warning")
|
||||
// disable access log
|
||||
xrayJsonConfig.getJSONObject("log").put("access", "none")
|
||||
|
||||
// replace socks address
|
||||
// (xrayJsonConfig.getJSONArray("inbounds")[0] as JSONObject).put("listen", "::1")
|
||||
|
||||
start(xrayConfig, xrayJsonConfig.toString(), vpnBuilder, protect)
|
||||
state.value = CONNECTED
|
||||
isRunning = true
|
||||
}
|
||||
|
||||
private fun parseConfig(config: JSONObject, xrayJsonConfig: JSONObject): XrayConfig {
|
||||
return XrayConfig.build {
|
||||
addAddress(XrayConfig.DEFAULT_IPV4_ADDRESS)
|
||||
|
||||
config.optString("dns1").let {
|
||||
if (it.isNotBlank()) addDnsServer(parseInetAddress(it))
|
||||
}
|
||||
|
||||
config.optString("dns2").let {
|
||||
if (it.isNotBlank()) addDnsServer(parseInetAddress(it))
|
||||
}
|
||||
|
||||
addRoute(InetNetwork("0.0.0.0", 0))
|
||||
addRoute(InetNetwork("2000::0", 3))
|
||||
config.getString("hostName").let {
|
||||
excludeRoute(InetNetwork(it, 32))
|
||||
}
|
||||
|
||||
config.optString("mtu").let {
|
||||
if (it.isNotBlank()) setMtu(it.toInt())
|
||||
}
|
||||
|
||||
val socksConfig = xrayJsonConfig.getJSONArray("inbounds")[0] as JSONObject
|
||||
socksConfig.getInt("port").let { setSocksPort(it) }
|
||||
|
||||
configSplitTunneling(config)
|
||||
configAppSplitTunneling(config)
|
||||
}
|
||||
}
|
||||
|
||||
private fun start(config: XrayConfig, configJson: String, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
buildVpnInterface(config, vpnBuilder)
|
||||
|
||||
DialerController { protect(it.toInt()) }.also {
|
||||
LibXray.registerDialerController(it).isNotNullOrBlank { err ->
|
||||
throw VpnStartException("Failed to register dialer controller: $err")
|
||||
}
|
||||
LibXray.registerListenerController(it).isNotNullOrBlank { err ->
|
||||
throw VpnStartException("Failed to register listener controller: $err")
|
||||
}
|
||||
}
|
||||
|
||||
vpnBuilder.establish().use { tunFd ->
|
||||
if (tunFd == null) {
|
||||
throw VpnStartException("Create VPN interface: permission not granted or revoked")
|
||||
}
|
||||
Log.d(TAG, "Run tun2Socks")
|
||||
runTun2Socks(config, tunFd.detachFd())
|
||||
|
||||
Log.d(TAG, "Run XRay")
|
||||
Log.i(TAG, "xray ${LibXray.xrayVersion()}")
|
||||
val assetsPath = context.getDir("assets", Context.MODE_PRIVATE).absolutePath
|
||||
LibXray.initXray(assetsPath)
|
||||
val geoDir = File(assetsPath, "geo").absolutePath
|
||||
val configPath = File(context.cacheDir, "config.json")
|
||||
Log.d(TAG, "xray.location.asset: $geoDir")
|
||||
Log.d(TAG, "config: $configPath")
|
||||
try {
|
||||
configPath.writeText(configJson)
|
||||
} catch (e: IOException) {
|
||||
LibXray.stopTun2Socks()
|
||||
throw VpnStartException("Failed to write xray config: ${e.message}")
|
||||
}
|
||||
LibXray.runXray(geoDir, configPath.absolutePath, config.maxMemory).isNotNullOrBlank { err ->
|
||||
LibXray.stopTun2Socks()
|
||||
throw VpnStartException("Failed to start xray: $err")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopVpn() {
|
||||
LibXray.stopXray().isNotNullOrBlank { err ->
|
||||
Log.e(TAG, "Failed to stop XRay: $err")
|
||||
}
|
||||
LibXray.stopTun2Socks().isNotNullOrBlank { err ->
|
||||
Log.e(TAG, "Failed to stop tun2Socks: $err")
|
||||
}
|
||||
|
||||
isRunning = false
|
||||
state.value = DISCONNECTED
|
||||
}
|
||||
|
||||
override fun reconnectVpn(vpnBuilder: Builder) {
|
||||
state.value = CONNECTED
|
||||
}
|
||||
|
||||
private fun runTun2Socks(config: XrayConfig, fd: Int) {
|
||||
val tun2SocksConfig = Tun2SocksConfig().apply {
|
||||
mtu = config.mtu.toLong()
|
||||
proxy = "socks5://127.0.0.1:${config.socksPort}"
|
||||
device = "fd://$fd"
|
||||
logLevel = "warning"
|
||||
}
|
||||
LibXray.startTun2Socks(tun2SocksConfig, fd.toLong()).isNotNullOrBlank { err ->
|
||||
throw VpnStartException("Failed to start tun2socks: $err")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String?.isNotNullOrBlank(block: (String) -> Unit) {
|
||||
if (!this.isNullOrBlank()) {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
42
client/android/xray/src/main/kotlin/XrayConfig.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package org.amnezia.vpn.protocol.xray
|
||||
|
||||
import org.amnezia.vpn.protocol.ProtocolConfig
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
|
||||
private const val XRAY_DEFAULT_MTU = 1500
|
||||
private const val XRAY_DEFAULT_MAX_MEMORY: Long = 50 shl 20 // 50 MB
|
||||
|
||||
class XrayConfig protected constructor(
|
||||
protocolConfigBuilder: ProtocolConfig.Builder,
|
||||
val socksPort: Int,
|
||||
val maxMemory: Long,
|
||||
) : ProtocolConfig(protocolConfigBuilder) {
|
||||
|
||||
protected constructor(builder: Builder) : this(
|
||||
builder,
|
||||
builder.socksPort,
|
||||
builder.maxMemory
|
||||
)
|
||||
|
||||
class Builder : ProtocolConfig.Builder(false) {
|
||||
internal var socksPort: Int = 0
|
||||
private set
|
||||
|
||||
internal var maxMemory: Long = XRAY_DEFAULT_MAX_MEMORY
|
||||
private set
|
||||
|
||||
override var mtu: Int = XRAY_DEFAULT_MTU
|
||||
|
||||
fun setSocksPort(port: Int) = apply { socksPort = port }
|
||||
|
||||
fun setMaxMemory(maxMemory: Long) = apply { this.maxMemory = maxMemory }
|
||||
|
||||
override fun build(): XrayConfig = configBuild().run { XrayConfig(this@Builder) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal val DEFAULT_IPV4_ADDRESS: InetNetwork = InetNetwork("10.0.42.2", 30)
|
||||
|
||||
inline fun build(block: Builder.() -> Unit): XrayConfig = Builder().apply(block).build()
|
||||
}
|
||||
}
|
||||
@@ -52,3 +52,6 @@ foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
|
||||
)
|
||||
endforeach()
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/xray/android/libxray.aar
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
||||
|
||||
@@ -69,6 +69,8 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
|
||||
|
||||
case DockerContainer::Sftp: return { Proto::Sftp };
|
||||
|
||||
case DockerContainer::Socks5Proxy: return { Proto::Socks5Proxy };
|
||||
|
||||
default: return { defaultProtocol(container) };
|
||||
}
|
||||
}
|
||||
@@ -97,8 +99,9 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
|
||||
{ DockerContainer::SSXray, "ShadowSocks"},
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
|
||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
|
||||
{ DockerContainer::Dns, QObject::tr("AmneziaDNS") },
|
||||
{ DockerContainer::Sftp, QObject::tr("SFTP file sharing service") },
|
||||
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
|
||||
}
|
||||
|
||||
QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
@@ -107,7 +110,7 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
||||
"own security protocol with SSL/TLS for key exchange.") },
|
||||
{ DockerContainer::ShadowSocks,
|
||||
QObject::tr("ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it "
|
||||
QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
|
||||
"may be recognized by analysis systems in some highly censored regions.") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
||||
@@ -124,14 +127,16 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
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 "
|
||||
QObject::tr("IKEv2/IPsec - 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.") },
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
|
||||
{ DockerContainer::Dns,
|
||||
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
|
||||
{ DockerContainer::Sftp,
|
||||
QObject::tr("Create a file vault on your server to securely store and transfer files.") } };
|
||||
QObject::tr("Create a file vault on your server to securely store and transfer files.") },
|
||||
{ DockerContainer::Socks5Proxy,
|
||||
QObject::tr("") } };
|
||||
}
|
||||
|
||||
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
@@ -159,7 +164,6 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
|
||||
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
|
||||
"* Available in the AmneziaVPN only on desktop platforms\n"
|
||||
"* Normal power consumption on mobile devices\n\n"
|
||||
"* Configurable encryption protocol\n"
|
||||
"* Detectable by some DPI systems\n"
|
||||
"* Works over TCP network protocol.") },
|
||||
@@ -240,7 +244,8 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
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.\" ") }
|
||||
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") },
|
||||
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -265,6 +270,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
|
||||
case DockerContainer::TorWebSite: return Proto::TorWebSite;
|
||||
case DockerContainer::Dns: return Proto::Dns;
|
||||
case DockerContainer::Sftp: return Proto::Sftp;
|
||||
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
|
||||
default: return Proto::Any;
|
||||
}
|
||||
}
|
||||
@@ -298,6 +304,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
case DockerContainer::ShadowSocks: return false;
|
||||
case DockerContainer::Awg: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
case DockerContainer::Xray: return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -367,6 +374,7 @@ bool ContainerProps::isShareable(DockerContainer container)
|
||||
case DockerContainer::TorWebSite: return false;
|
||||
case DockerContainer::Dns: return false;
|
||||
case DockerContainer::Sftp: return false;
|
||||
case DockerContainer::Socks5Proxy: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ namespace amnezia
|
||||
// non-vpn
|
||||
TorWebSite,
|
||||
Dns,
|
||||
Sftp
|
||||
Sftp,
|
||||
Socks5Proxy
|
||||
};
|
||||
Q_ENUM_NS(DockerContainer)
|
||||
} // namespace ContainerEnumNS
|
||||
|
||||
@@ -40,6 +40,28 @@ void ApiController::processApiConfig(const QString &protocol, const ApiControlle
|
||||
return;
|
||||
} else if (protocol == configKey::awg) {
|
||||
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||
auto serverConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
auto containers = serverConfig.value(config_key::containers).toArray();
|
||||
if (containers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto container = containers.at(0).toObject();
|
||||
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||
auto containerConfig = container.value(containerName).toObject();
|
||||
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
|
||||
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
|
||||
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
|
||||
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
|
||||
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
|
||||
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
|
||||
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
container[containerName] = containerConfig;
|
||||
containers.replace(0, container);
|
||||
serverConfig[config_key::containers] = containers;
|
||||
config = QString(QJsonDocument(serverConfig).toJson());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
|
||||
QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
|
||||
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
|
||||
@@ -376,6 +376,10 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Socks5Proxy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -516,6 +520,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
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();
|
||||
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
|
||||
|
||||
Vars vars;
|
||||
|
||||
@@ -613,6 +618,14 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||
|
||||
// Socks5 proxy vars
|
||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||
auto username = socks5ProxyConfig.value(config_key:: userName).toString();
|
||||
auto password = socks5ProxyConfig.value(config_key::password).toString();
|
||||
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
|
||||
vars.append({ { "$SOCKS5_USER", socks5user } });
|
||||
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
|
||||
|
||||
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
|
||||
if (!serverIp.isEmpty()) {
|
||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||
|
||||
@@ -9,7 +9,7 @@ QString errorString(ErrorCode code) {
|
||||
|
||||
// General error codes
|
||||
case(ErrorCode::NoError): errorMessage = QObject::tr("No error"); break;
|
||||
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown Error"); break;
|
||||
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); break;
|
||||
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
|
||||
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
|
||||
|
||||
@@ -23,15 +23,15 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break;
|
||||
case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break;
|
||||
case(ErrorCode::SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break;
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("SSH request was interrupted"); break;
|
||||
case(ErrorCode::SshInternalError): errorMessage = QObject::tr("SSH internal error"); break;
|
||||
case(ErrorCode::SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break;
|
||||
case(ErrorCode::SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break;
|
||||
case(ErrorCode::SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break;
|
||||
|
||||
// Ssh scp errors
|
||||
case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break;
|
||||
case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("SCP error: Generic failure"); break;
|
||||
|
||||
// Local errors
|
||||
case (ErrorCode::OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break;
|
||||
@@ -39,7 +39,7 @@ QString errorString(ErrorCode code) {
|
||||
|
||||
// Distro errors
|
||||
case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break;
|
||||
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break;
|
||||
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("Shadowsocks (ss-local) executable missing"); break;
|
||||
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
|
||||
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
|
||||
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
|
||||
|
||||
@@ -18,6 +18,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
||||
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
|
||||
case DockerContainer::Dns: return QLatin1String("dns");
|
||||
case DockerContainer::Sftp: return QLatin1String("sftp");
|
||||
case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy");
|
||||
default: return QString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
|
||||
|
||||
{ Proto::TorWebSite, "Website in Tor network" },
|
||||
{ Proto::Dns, "DNS Service" },
|
||||
{ Proto::Sftp, QObject::tr("Sftp service") } };
|
||||
{ Proto::Sftp, QObject::tr("SFTP service") },
|
||||
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
|
||||
}
|
||||
|
||||
QMap<amnezia::Proto, QString> ProtocolProps::protocolDescriptions()
|
||||
@@ -102,6 +103,7 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
|
||||
case Proto::TorWebSite: return ServiceType::Other;
|
||||
case Proto::Dns: return ServiceType::Other;
|
||||
case Proto::Sftp: return ServiceType::Other;
|
||||
case Proto::Socks5Proxy: return ServiceType::Other;
|
||||
default: return ServiceType::Other;
|
||||
}
|
||||
}
|
||||
@@ -113,6 +115,7 @@ int ProtocolProps::getPortForInstall(Proto p)
|
||||
case WireGuard:
|
||||
case ShadowSocks:
|
||||
case OpenVpn:
|
||||
case Socks5Proxy:
|
||||
return QRandomGenerator::global()->bounded(30000, 50000);
|
||||
default:
|
||||
return defaultPort(p);
|
||||
@@ -135,6 +138,7 @@ int ProtocolProps::defaultPort(Proto p)
|
||||
case Proto::TorWebSite: return -1;
|
||||
case Proto::Dns: return 53;
|
||||
case Proto::Sftp: return 222;
|
||||
case Proto::Socks5Proxy: return 38080;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
@@ -154,6 +158,7 @@ bool ProtocolProps::defaultPortChangeable(Proto p)
|
||||
case Proto::TorWebSite: return false;
|
||||
case Proto::Dns: return false;
|
||||
case Proto::Sftp: return true;
|
||||
case Proto::Socks5Proxy: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -175,6 +180,7 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
|
||||
case Proto::TorWebSite: return TransportProto::Tcp;
|
||||
case Proto::Dns: return TransportProto::Udp;
|
||||
case Proto::Sftp: return TransportProto::Tcp;
|
||||
case Proto::Socks5Proxy: return TransportProto::Tcp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +201,7 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
|
||||
case Proto::TorWebSite: return false;
|
||||
case Proto::Dns: return false;
|
||||
case Proto::Sftp: return false;
|
||||
case Proto::Socks5Proxy: return false;
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace amnezia
|
||||
constexpr char awg[] = "awg";
|
||||
constexpr char xray[] = "xray";
|
||||
constexpr char ssxray[] = "ssxray";
|
||||
constexpr char socks5proxy[] = "socks5proxy";
|
||||
|
||||
constexpr char configVersion[] = "config_version";
|
||||
|
||||
@@ -216,6 +217,14 @@ namespace amnezia
|
||||
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
||||
}
|
||||
|
||||
namespace socks5Proxy
|
||||
{
|
||||
constexpr char defaultUserName[] = "proxy_user";
|
||||
constexpr char defaultPort[] = "38080";
|
||||
|
||||
constexpr char proxyConfigPath[] = "/usr/local/3proxy/conf/3proxy.cfg";
|
||||
}
|
||||
|
||||
} // namespace protocols
|
||||
|
||||
namespace ProtocolEnumNS
|
||||
@@ -244,7 +253,8 @@ namespace amnezia
|
||||
// non-vpn
|
||||
TorWebSite,
|
||||
Dns,
|
||||
Sftp
|
||||
Sftp,
|
||||
Socks5Proxy
|
||||
};
|
||||
Q_ENUM_NS(Proto)
|
||||
|
||||
|
||||
@@ -198,7 +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/PageProtocolXraySettings.qml</file>
|
||||
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
|
||||
@@ -239,5 +239,10 @@
|
||||
<file>images/controls/alert-circle.svg</file>
|
||||
<file>images/controls/file-check-2.svg</file>
|
||||
<file>ui/qml/Controls2/WarningType.qml</file>
|
||||
<file>ui/qml/Pages2/PageServiceSocksProxySettings.qml</file>
|
||||
<file>server_scripts/socks5_proxy/run_container.sh</file>
|
||||
<file>server_scripts/socks5_proxy/Dockerfile</file>
|
||||
<file>server_scripts/socks5_proxy/configure_container.sh</file>
|
||||
<file>server_scripts/socks5_proxy/start.sh</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
sudo iptables -C INPUT -p icmp --icmp-type echo-request -j DROP || sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
|
||||
|
||||
#sudo iptables -P FORWARD ACCEPT
|
||||
sudo iptables -C FORWARD -j DOCKER-USER || sudo iptables -A FORWARD -j DOCKER-USER
|
||||
sudo iptables -C FORWARD -j DOCKER-ISOLATION-STAGE-1 || sudo iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1; \
|
||||
sudo iptables -C FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT || sudo iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; \
|
||||
sudo iptables -C FORWARD -o docker0 -j DOCKER || sudo iptables -A FORWARD -o docker0 -j DOCKER; \
|
||||
sudo iptables -C FORWARD -i docker0 ! -o docker0 -j ACCEPT || sudo iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT; \
|
||||
sudo iptables -C FORWARD -i docker0 -o docker0 -j ACCEPT || sudo iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT
|
||||
|
||||
# Tuning network
|
||||
sudo sysctl fs.file-max=51200; \
|
||||
sudo sysctl net.core.rmem_max=67108864; \
|
||||
sudo sysctl net.core.wmem_max=67108864; \
|
||||
sudo sysctl net.core.netdev_max_backlog=250000; \
|
||||
sudo sysctl net.core.somaxconn=4096; \
|
||||
sudo sysctl net.ipv4.tcp_syncookies=1; \
|
||||
sudo sysctl net.ipv4.tcp_tw_reuse=1; \
|
||||
sudo sysctl net.ipv4.tcp_tw_recycle=0; \
|
||||
sudo sysctl net.ipv4.tcp_fin_timeout=30; \
|
||||
sudo sysctl net.ipv4.tcp_keepalive_time=1200; \
|
||||
sudo sysctl net.ipv4.ip_local_port_range="10000 65000"; \
|
||||
sudo sysctl net.ipv4.tcp_max_syn_backlog=8192; \
|
||||
sudo sysctl net.ipv4.tcp_max_tw_buckets=5000; \
|
||||
sudo sysctl net.ipv4.tcp_fastopen=3; \
|
||||
sudo sysctl net.ipv4.tcp_mem="25600 51200 102400"; \
|
||||
sudo sysctl net.ipv4.tcp_rmem="4096 87380 67108864"; \
|
||||
sudo sysctl net.ipv4.tcp_wmem="4096 65536 67108864"; \
|
||||
sudo sysctl net.ipv4.tcp_mtu_probing=1; \
|
||||
sudo sysctl -w net.ipv4.ip_forward=1;\
|
||||
sudo iptables -C INPUT -p icmp --icmp-type echo-request -j DROP || sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP;\
|
||||
sudo iptables -C FORWARD -j DOCKER-USER || sudo iptables -A FORWARD -j DOCKER-USER;\
|
||||
sudo iptables -C FORWARD -j DOCKER-ISOLATION-STAGE-1 || sudo iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1;\
|
||||
sudo iptables -C FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT || sudo iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT;\
|
||||
sudo iptables -C FORWARD -o docker0 -j DOCKER || sudo iptables -A FORWARD -o docker0 -j DOCKER;\
|
||||
sudo iptables -C FORWARD -i docker0 ! -o docker0 -j ACCEPT || sudo iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT;\
|
||||
sudo iptables -C FORWARD -i docker0 -o docker0 -j ACCEPT || sudo iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT;\
|
||||
sudo sysctl fs.file-max=51200;\
|
||||
sudo sysctl net.core.rmem_max=67108864;\
|
||||
sudo sysctl net.core.wmem_max=67108864;\
|
||||
sudo sysctl net.core.netdev_max_backlog=250000;\
|
||||
sudo sysctl net.core.somaxconn=4096;\
|
||||
sudo sysctl net.ipv4.tcp_syncookies=1;\
|
||||
sudo sysctl net.ipv4.tcp_tw_reuse=1;\
|
||||
sudo sysctl net.ipv4.tcp_tw_recycle=0;\
|
||||
sudo sysctl net.ipv4.tcp_fin_timeout=30;\
|
||||
sudo sysctl net.ipv4.tcp_keepalive_time=1200;\
|
||||
sudo sysctl net.ipv4.ip_local_port_range="10000 65000";\
|
||||
sudo sysctl net.ipv4.tcp_max_syn_backlog=8192;\
|
||||
sudo sysctl net.ipv4.tcp_max_tw_buckets=5000;\
|
||||
sudo sysctl net.ipv4.tcp_fastopen=3;\
|
||||
sudo sysctl net.ipv4.tcp_mem="25600 51200 102400";\
|
||||
sudo sysctl net.ipv4.tcp_rmem="4096 87380 67108864";\
|
||||
sudo sysctl net.ipv4.tcp_wmem="4096 65536 67108864";\
|
||||
sudo sysctl net.ipv4.tcp_mtu_probing=1;\
|
||||
sudo sysctl net.ipv4.tcp_congestion_control=hybla
|
||||
|
||||
10
client/server_scripts/socks5_proxy/Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM 3proxy/3proxy:latest
|
||||
|
||||
LABEL maintainer="AmneziaVPN"
|
||||
|
||||
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
|
||||
|
||||
ENTRYPOINT [ "/bin/sh", "/opt/amnezia/start.sh" ]
|
||||
CMD [ "" ]
|
||||
12
client/server_scripts/socks5_proxy/configure_container.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo -e "#!/bin/3proxy" > /usr/local/3proxy/conf/3proxy.cfg
|
||||
echo -e "config /usr/local/3proxy/conf/3proxy.cfg" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
echo -e "timeouts 1 5 30 60 180 1800 15 60" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
|
||||
echo -e "$SOCKS5_USER" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
|
||||
echo -e "log /usr/local/3proxy/logs/3proxy.log" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
echo -e "logformat \"-\\\"\"+_G{\"\"time_unix\"\":%t, \"\"proxy\"\":{\"\"type:\"\":\"\"%N\"\", \"\"port\"\":%p}, \"\"error\"\":{\"\"code\"\":\"\"%E\"\"}, \"\"auth\"\":{\"\"user\"\":\"\"%U\"\"}, \"\"client\"\":{\"\"ip\"\":\"\"%C\"\", \"\"port\"\":%c}, \"\"server\"\":{\"\"ip\"\":\"\"%R\"\", \"\"port\"\":%r}, \"\"bytes\"\":{\"\"sent\"\":%O, \"\"received\"\":%I}, \"\"request\"\":{\"\"hostname\"\":\"\"%n\"\"}, \"\"message\"\":\"\"%T\"\"}\"" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
echo -e "auth $SOCKS5_AUTH_TYPE" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
echo -e "socks -p$SOCKS5_PROXY_PORT" >> /usr/local/3proxy/conf/3proxy.cfg
|
||||
5
client/server_scripts/socks5_proxy/run_container.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
sudo docker run -d \
|
||||
--restart always \
|
||||
-p $SOCKS5_PROXY_PORT:$SOCKS5_PROXY_PORT/tcp \
|
||||
--name $CONTAINER_NAME \
|
||||
$CONTAINER_NAME
|
||||
7
client/server_scripts/socks5_proxy/start.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
|
||||
|
||||
echo "Container startup"
|
||||
|
||||
/bin/3proxy /usr/local/3proxy/conf/3proxy.cfg
|
||||
@@ -39,7 +39,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>تم تحديث الاعدادات بنجاح, جاري إعادة الاتصال...</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -700,8 +700,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<translation>ShadowSocks إعدادات</translation>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>Shadowsocks إعدادات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
|
||||
@@ -1041,8 +1041,8 @@ And if you don't like the app, all the more support it - the donation will
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
|
||||
<source>Github</source>
|
||||
<translation></translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
|
||||
@@ -1317,7 +1317,7 @@ And if you don't like the app, all the more support it - the donation will
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>الخادم الافتراضي لا يدعم DNS مخصص</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1390,7 +1390,7 @@ And if you don't like the app, all the more support it - the donation will
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>سيتم حفظ سجلات البرنامج بشكل تلقائي عند تفعيل هذه الميزة, بشكل افتراضي, هذه الميزة مٌعطلة. قم بتفعيل هذه الميزة في حالة هناك خلل في التطبيق.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1797,7 +1797,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>رمز QR</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2089,8 +2089,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<translation>احفظ تكوين ShadowSocks</translation>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>احفظ تكوين Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
||||
@@ -2119,8 +2119,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<translation>تنسيق ShadowSocks الاصلي</translation>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>تنسيق Shadowsocks الاصلي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
|
||||
@@ -2571,8 +2571,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="77"/>
|
||||
<source>Sftp service</source>
|
||||
<translation>خدمة Sftp</translation>
|
||||
<source>SFTP service</source>
|
||||
<translation>خدمة SFTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="11"/>
|
||||
@@ -2581,7 +2581,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<source>Unknown error</source>
|
||||
<translation>خطأ غير معروف</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2621,18 +2621,18 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="24"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>طلب Ssh محظو</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>طلب SSH محظو</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>إنقطع طلب Ssh</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>إنقطع طلب SSH</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>مشكلة داخلية Ssh</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>مشكلة داخلية SSH</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
@@ -2720,7 +2720,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2735,8 +2735,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="40"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<translation>ShadowSocks (ss-local) executable مفقود</translation>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>Shadowsocks (ss-local) executable مفقود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
@@ -2821,13 +2821,13 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="91"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation></translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="92"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>ملف Sftp: خدمة المشاركة</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>ملف SFTP: خدمة المشاركة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
@@ -2836,8 +2836,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="101"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>بروتوكول ShadowSocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>بروتوكول Shadowsocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية
|
||||
ولكن قد يتم التعرف عليه من خلال أنظمة التحليل في بعض المناطق شديدة الرقابة.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2944,8 +2944,8 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>بروتوكول IKEv2 - بروتوكول مستقر حديث, اسرع بقليل من الباقي, يسترجع الاتصال بعد خسارة الاشارة.</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>بروتوكول IKEv2/IPsec - بروتوكول مستقر حديث, اسرع بقليل من الباقي, يسترجع الاتصال بعد خسارة الاشارة.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
@@ -2981,16 +2981,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>Shadowsocks, مستوحي من بروتوكول SOCKS5, يحمي الاتصال بأستعمال شفرة AEAD. كذلك Shadowsocks صٌمم كي يكون متحفظاً ويصعب تحديدة, إنه ليس مطابقًا لاتصال HTTPS القياسي. عمتاُ. بعض انظمة تحليل حركات المرور قد تتعرف علي اتصال Shadowsocks. بسبب الدعم المحدود في Amnezia, يٌنصح بأستخدام بروتوكول AmneziaWG.
|
||||
|
||||
* مٌتاح في AmneziaVPN عبر جميع المنصات
|
||||
* استهلاك طاقة عادي علي اجهزة المحمول
|
||||
|
||||
* بروتوكول تشفير قابل للتكوين
|
||||
* قابل للكشف بواسطة بعض انظمة DPI
|
||||
* يعمل عبر بروتوكول شبكة TCP.</translation>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>تنظیمات به روز رسانی شد
|
||||
در حال اتصال دوباره...</translation>
|
||||
</message>
|
||||
@@ -664,8 +664,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<translation>تنظیمات ShadowSocks</translation>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>تنظیمات Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
|
||||
@@ -1002,8 +1002,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
|
||||
<source>Github</source>
|
||||
<translation>Github</translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
|
||||
@@ -1250,7 +1250,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>سرور پیشفرض از دیاناس سفارشی پشتیبانی نمیکند</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1323,7 +1323,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>فعال کردن این عملکرد باعث ذخیره خودکار لاگهای برنامه میشود. به طور پیشفرض، قابلیت ثبت لاگ غیرفعال است. در صورت بروز خطا در برنامه، ذخیره لاگ را فعال کنید.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1710,7 +1710,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>QR-Code</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2037,8 +2037,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<translation>ذخیره تنظیمات ShadowSocks</translation>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>ذخیره تنظیمات Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
||||
@@ -2057,8 +2057,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<translation>فرمت ShadowSocks</translation>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>فرمت Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
|
||||
@@ -2476,8 +2476,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<translation>Unknown Error</translation>
|
||||
<source>Unknown error</source>
|
||||
<translation>Unknown error</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="13"/>
|
||||
@@ -2516,18 +2516,18 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="24"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>Ssh request was denied</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH request was denied</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>Ssh request was interrupted</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH request was interrupted</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>Ssh internal error</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>SSH internal error</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
@@ -2628,7 +2628,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2643,8 +2643,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="40"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<translation>ShadowSocks (ss-local) executable missing</translation>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>Shadowsocks (ss-local) executable missing</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
@@ -2718,7 +2718,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="101"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>شدوساکس - ترافیک VPN را پنهان می کند، به طوری که مشابه ترافیک وب عادی می شود، اما ممکن است توسط سیستم های تجزیه و تحلیل در برخی از مناطق با سانسور شدید شناسایی شود.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2819,8 +2819,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="92"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>سرویس فایل اشتراک Sftp</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>سرویس فایل اشتراک SFTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="90"/>
|
||||
@@ -2830,8 +2830,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="91"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation>Amnezia DNS</translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
@@ -2850,8 +2850,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>پروتکل IKEv2 پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکلهاست. بعد از قطع سیگنال دوباره اتصال را بازیابی میکند.</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>پروتکل IKEv2/IPsec پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکلهاست. بعد از قطع سیگنال دوباره اتصال را بازیابی میکند.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
@@ -2891,15 +2891,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>پروتکل Shadowsocks، الهام گرفته از پروتکل Socks5، اتصال را با استفاده از رمزگذاری AEAD امن میکند. اگرچه Shadowsocks طوری طراحی شده که برای شناسایی در شبکه چالشبرانگیز باشد و محتاط عمل کند اما این پروتکل مانند یک اتصال استاندارد HTTPS نیست و برخی از سیستمهای تحلیل ترافیک مشخص ممکن است بتوانند اتصال Shadowsocks را شناسایی کنند. به دلیل محدودیت پشتیبانی در Amnezia پیشنهاد میشود که از َAmneziaWG استفاده شود.
|
||||
|
||||
* فقط بر روی پلتفرم دسکتاپ بر روی Amnezia قابل دسترس است
|
||||
* مصرف انرژی عادی در دستگاههای موبایل
|
||||
* پروتکل رمزنگاری قابل پیکربندی
|
||||
* قابل شناسایی توسط برخی سیستمهای تحلیل عمیق DPI
|
||||
* عملکرد بر روی پروتکل شبکه TCP</translation>
|
||||
@@ -2946,8 +2943,8 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="77"/>
|
||||
<source>Sftp service</source>
|
||||
<translation>سرویس Sftp</translation>
|
||||
<source>SFTP service</source>
|
||||
<translation>سرویس SFTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="162"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>सेटिंग्स सफलतापूर्वक अपडेट हो गईं...</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -711,7 +711,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="95"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>शैडोसॉक्स सेटिंग्स</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1055,13 +1055,13 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="140"/>
|
||||
<source>Github</source>
|
||||
<translation>Github</translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="147"/>
|
||||
<source>https://github.com/amnezia-vpn/amnezia-client</source>
|
||||
<translation></translation>
|
||||
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="158"/>
|
||||
@@ -1144,7 +1144,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/>
|
||||
<source>Executable file (*.*)</source>
|
||||
<translation>निष्पादनीय फाइल (*।*)</translation>
|
||||
<translation>निष्पादनीय फाइल (*.*)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1276,7 +1276,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="102"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="134"/>
|
||||
<source>Backup files (*.backup)</source>
|
||||
<translation>बैकअप फ़ाइलें (*.बैकअप)</translation>
|
||||
<translation>बैकअप फ़ाइलें (*.backup)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="111"/>
|
||||
@@ -1386,7 +1386,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="46"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>डिफ़ॉल्ट सर्वर कस्टम डीएनएस का समर्थन नहीं करता है</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1459,7 +1459,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="69"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>इस फ़ंक्शन को सक्षम करने से एप्लिकेशन के लॉग स्वचालित रूप से सहेजे जाएंगे, डिफ़ॉल्ट रूप से, लॉगिंग कार्यक्षमता अक्षम है। एप्लिकेशन की खराबी की स्थिति में लॉग सेविंग सक्षम करें.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1480,7 +1480,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="143"/>
|
||||
<source>Logs files (*.log)</source>
|
||||
<translation>लॉग फ़ाइलें (*.लॉग)</translation>
|
||||
<translation>लॉग फ़ाइलें (*.log)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="152"/>
|
||||
@@ -1868,7 +1868,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="102"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>क्यू आर संहिता</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2164,7 +2164,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>शैडोसॉक्स कॉन्फ़िगरेशन सहेजें</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2199,7 +2199,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>शैडोसॉक्स मूल प्रारूप</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2663,7 +2663,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="78"/>
|
||||
<source>Sftp service</source>
|
||||
<source>SFTP service</source>
|
||||
<translation>एसएफटीपी सेवा</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2673,7 +2673,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<source>Unknown error</source>
|
||||
<translation>अज्ञात त्रुटि</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2718,18 +2718,18 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>Ssh अनुरोध अस्वीकार कर दिया गया</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH अनुरोध अस्वीकार कर दिया गया</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>Ssh अनुरोध बाधित हो गया था</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH अनुरोध बाधित हो गया था</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>Ssh आंतरिक त्रुटि</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>SSH आंतरिक त्रुटि</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="28"/>
|
||||
@@ -2773,7 +2773,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="33"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation>एससीपी त्रुटि: सामान्य विफलता</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2788,7 +2788,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>शैडोसॉक्स (एसएस-स्थानीय) निष्पादन योग्य गायब है</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2874,13 +2874,13 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="97"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation></translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>Sftp फ़ाइल साझाकरण सेवा</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>SFTP फ़ाइल साझाकरण सेवा</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="104"/>
|
||||
@@ -2889,7 +2889,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="107"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>शैडोसॉक्स - वीपीएन ट्रैफ़िक को मास्क करता है, जिससे यह सामान्य वेब ट्रैफ़िक के समान हो जाता है, लेकिन इसे कुछ अत्यधिक सेंसर किए गए क्षेत्रों में विश्लेषण प्रणालियों द्वारा पहचाना जा सकता है.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2998,8 +2998,8 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="124"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2 - आधुनिक स्थिर प्रोटोकॉल, दूसरों की तुलना में थोड़ा तेज़, सिग्नल हानि के बाद कनेक्शन पुनर्स्थापित करता है।</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2/IPsec - आधुनिक स्थिर प्रोटोकॉल, दूसरों की तुलना में थोड़ा तेज़, सिग्नल हानि के बाद कनेक्शन पुनर्स्थापित करता है।</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="127"/>
|
||||
@@ -3035,16 +3035,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>शैडोसॉक्स, SOCKS5 प्रोटोकॉल से प्रेरित होकर, AEAD सिफर का उपयोग करके कनेक्शन की सुरक्षा करता है। हालाँकि शैडोसॉक्स को विवेकपूर्ण और पहचानने में चुनौतीपूर्ण बनाया गया है, यह एक मानक HTTPS कनेक्शन के समान नहीं है। हालाँकि, कुछ ट्रैफ़िक विश्लेषण प्रणालियाँ अभी भी शैडोसॉक्स कनेक्शन का पता लगा सकती हैं। Amnezia में सीमित समर्थन के कारण, AmneziaWG प्रोटोकॉल का उपयोग करने की अनुशंसा की जाती है।
|
||||
|
||||
* AmneziaVPN केवल डेस्कटॉप प्लेटफ़ॉर्म पर उपलब्ध है
|
||||
* मोबाइल उपकरणों पर सामान्य बिजली की खपत
|
||||
|
||||
* कॉन्फ़िगर करने योग्य एन्क्रिप्शन प्रोटोकॉल
|
||||
* कुछ डीपीआई सिस्टम द्वारा पता लगाने योग्य
|
||||
* टीसीपी नेटवर्क प्रोटोकॉल पर काम करता है।.</translation>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ၊ ပြန်လည်ချိတ်ဆက်နေပါသည်...</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -664,8 +664,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<translation>ShadowSocks ဆက်တင်များ</translation>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>Shadowsocks ဆက်တင်များ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
|
||||
@@ -1002,8 +1002,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
|
||||
<source>Github</source>
|
||||
<translation>Github</translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
|
||||
@@ -1250,8 +1250,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<translation>မူရင်းဆာဗာသည် စိတ်ကြိုက် dns ကို အထောက်အပံ့မပေးပါ</translation>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>မူရင်းဆာဗာသည် စိတ်ကြိုက် DNS ကို အထောက်အပံ့မပေးပါ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
|
||||
@@ -1323,7 +1323,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>ဤလုပ်ဆောင်ချက်ကို ဖွင့်ခြင်းဖြင့် အပလီကေးရှင်း၏ မှတ်တမ်းများကို အလိုအလျောက် သိမ်းဆည်းပေးမည် ဖြစ်ပြီး မူရင်းအတိုင်း၊ မှတ်တမ်းလုပ်ဆောင်ချက်ကို ပိတ်ထားသည်။ အပလီကေးရှင်းချို့ယွင်းချက်ရှိသောအခါ မှတ်တမ်းသိမ်းဆည်းခြင်းကို ဖွင့်ပါ။</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1711,7 +1711,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>QR-ကုဒ်</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2038,8 +2038,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<translation>ShadowSocks config ကိုသိမ်းဆည်းမည်</translation>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>Shadowsocks config ကိုသိမ်းဆည်းမည်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
||||
@@ -2058,8 +2058,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<translation>ShadowSocks မူရင်းဖောမတ်</translation>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>Shadowsocks မူရင်းဖောမတ်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
|
||||
@@ -2477,7 +2477,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<source>Unknown error</source>
|
||||
<translation>အမည်မသိ မှားယွင်းမှု</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2517,18 +2517,18 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="24"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>Ssh တောင်းဆိုမှု ငြင်းဆိုခံလိုက်ရပါသည်</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH တောင်းဆိုမှု ငြင်းဆိုခံလိုက်ရပါသည်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>Ssh တောင်းဆိုမှု အနှောက်အယက်ခံလိုက်ရပါသည်</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH တောင်းဆိုမှု အနှောက်အယက်ခံလိုက်ရပါသည်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>စက်တွင်းဖြစ်သော Ssh မှားယွင်းမှု</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>စက်တွင်းဖြစ်သော SSH မှားယွင်းမှု</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
@@ -2624,7 +2624,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2639,8 +2639,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="40"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<translation>ShadowSocks (ss-local) executable ပျောက်နေပါသည်</translation>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>Shadowsocks (ss-local) executable ပျောက်နေပါသည်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
@@ -2719,8 +2719,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="101"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>ShadowSocks - ၎င်းသည် ပုံမှန်ဝဘ်လမ်းကြောင်းနှင့် ဆင်တူစေရန် VPN အသွားအလာကို ဖုံးကွယ်ထားသော်လည်း ၎င်းကို အချို့သော ဆင်ဆာဖြတ်ထားသော ဒေသများရှိ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းသိရှိနိုင်ပါသည်.</translation>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>Shadowsocks - ၎င်းသည် ပုံမှန်ဝဘ်လမ်းကြောင်းနှင့် ဆင်တူစေရန် VPN အသွားအလာကို ဖုံးကွယ်ထားသော်လည်း ၎င်းကို အချို့သော ဆင်ဆာဖြတ်ထားသော ဒေသများရှိ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းသိရှိနိုင်ပါသည်.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="104"/>
|
||||
@@ -2820,8 +2820,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="92"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>Sftp ဖိုင်မျှဝေခြင်းဆားဗစ်</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>SFTP ဖိုင်မျှဝေခြင်းဆားဗစ်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="90"/>
|
||||
@@ -2831,8 +2831,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="91"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation>Amnezia DNS</translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
@@ -2851,8 +2851,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2 - ခေတ်မီတည်ငြိမ်သောပရိုတိုကော၊ အခြားအရာများထက်အနည်းငယ်ပိုမြန်သည်၊ signal ပျောက်ဆုံးပြီးနောက် ချိတ်ဆက်မှုကို ပြန်လည်ရယူပေးသည်.</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2/IPsec - ခေတ်မီတည်ငြိမ်သောပရိုတိုကော၊ အခြားအရာများထက်အနည်းငယ်ပိုမြန်သည်၊ signal ပျောက်ဆုံးပြီးနောက် ချိတ်ဆက်မှုကို ပြန်လည်ရယူပေးသည်.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
@@ -2888,16 +2888,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>SOCKS5 ပရိုတိုကောကို အတုယူအခြေခံတည်ဆောက်ထားသော Shadowsocks သည် AEAD cipher ကိုအသုံးပြု၍ ချိတ်ဆက်မှုကိုကာကွယ်ပေးသည်။ Shadowsocks သည် ထောက်လှန်းသိရှိခံရခြင်းမှရှောင်ရှားနိုင်ရန်နှင့် ထောက်လှန်းသည့်သူများခက်ခဲစေရန် ဒီဇိုင်းထုတ်ထားသော်လည်း စံသတ်မှတ်ထားသည့် HTTPS ချိတ်ဆက်မှုနှင့် ထပ်တူမကျပါ။ သို့သော်၊ အချို့သောလမ်းကြောင်းဆိုင်ရာ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များသည် Shadowsocks ချိတ်ဆက်မှုကို ရှာဖွေတွေ့ရှိနိုင်သေးသည်။ Amnezia တွင် ထောက်ပံ့မှုအကန့်အသတ်ရှိသောကြောင့် AmneziaWG ပရိုတိုကောကို အသုံးပြုရန် အကြံပြုထားသည်။
|
||||
|
||||
* Desktop ပလပ်ဖောင်းများတွင်ရှိသော AmneziaVPN တွင်သာအသုံးပြုနိုင်ပါသည်။
|
||||
* မိုဘိုင်းစက်ပစ္စည်းများတွင် ပုံမှန်ပါဝါသုံးစွဲမှုရှိခြင်း။
|
||||
|
||||
* ပြင်ဆင်သတ်မှတ်နိုင်သော စာဝှက်စနစ် ပရိုတိုကော
|
||||
* အချို့သော DPI စနစ်များဖြင့် ထောက်လှန်းသိရှိနိုင်သည်။
|
||||
* TCP ကွန်ရက် ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။.</translation>
|
||||
@@ -2944,8 +2940,8 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="77"/>
|
||||
<source>Sftp service</source>
|
||||
<translation>Sftp ဝန်ဆောင်မှု</translation>
|
||||
<source>SFTP service</source>
|
||||
<translation>SFTP ဝန်ဆောင်မှု</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="161"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<translation>Налаштування оновлено, Підключення...</translation>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>Налаштування оновлено, підключення...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="164"/>
|
||||
@@ -771,8 +771,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<translation>Налаштування ShadowSocks</translation>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>Налаштування Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="91"/>
|
||||
@@ -1138,8 +1138,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
|
||||
<source>Github</source>
|
||||
<translation>Github</translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
|
||||
@@ -1413,7 +1413,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="50"/>
|
||||
<source>Use AmneziaDNS</source>
|
||||
<translation>Використовувати Amnezia DNS</translation>
|
||||
<translation>Використовувати AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="51"/>
|
||||
@@ -1459,7 +1459,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation type="unfinished">Сервер за замовчуванням не підтримує користувацький DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1536,7 +1536,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="60"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1975,7 +1975,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>QR-код</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2368,8 +2368,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<translation type="unfinished">Зберегти конфігурацію ShadowSocks</translation>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation type="unfinished">Зберегти конфігурацію Shadowsocks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="88"/>
|
||||
@@ -2393,8 +2393,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<translation type="unfinished">ShadowSocks нативний формат</translation>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation type="unfinished">Shadowsocks нативний формат</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="145"/>
|
||||
@@ -2820,8 +2820,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<translation>Unknown Error</translation>
|
||||
<source>Unknown error</source>
|
||||
<translation>Unknown error</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="13"/>
|
||||
@@ -2865,18 +2865,18 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>Ssh request was denied</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH request was denied</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>Ssh request was interrupted</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH request was interrupted</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>Ssh internal error</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>SSH internal error</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="28"/>
|
||||
@@ -2895,7 +2895,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="33"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2991,8 +2991,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<translation>ShadowSocks (ss-local) executable missing</translation>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>Shadowsocks (ss-local) executable missing</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="42"/>
|
||||
@@ -3071,13 +3071,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="107"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation type="unfinished">ShadowSocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури.</translation>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation type="unfinished">Shadowsocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="110"/>
|
||||
<source>OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship.</source>
|
||||
<translation type="unfinished">OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probbing. Підходить для регіонів з самим високим рівнем цензури.</translation>
|
||||
<translation type="unfinished">OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probing. Підходить для регіонів з самим високим рівнем цензури.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="131"/>
|
||||
@@ -3142,7 +3142,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
Він може швидко переключись між мережами та пристроями, що робить його осболиво адаптованим під динамічні мережеві середовища.
|
||||
Потрібно зазначити, що незважаючи на стабільність та швидкість, IKEv2 легко розпізнається та вразливий до блокувань.
|
||||
|
||||
* IKEv2 в AmneziaVPN тільки для Windows.
|
||||
* IKEv2/IPsec в AmneziaVPN тільки для Windows.
|
||||
* Низьке енергоспоживання, на мобільних пристроях
|
||||
* Мінімальна конфігурація
|
||||
* Розпізнається системами DPI-анализу
|
||||
@@ -3155,8 +3155,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>Сервіс обміну файлами Sftp</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>Сервіс обміну файлами SFTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="96"/>
|
||||
@@ -3166,8 +3166,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="97"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation>Amnezia DNS</translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="104"/>
|
||||
@@ -3191,8 +3191,8 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="124"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2 сучасний стабільний протокол, трішки швидше за інших відновлює підключення.</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2/IPsec сучасний стабільний протокол, трішки швидше за інших відновлює підключення.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="127"/>
|
||||
@@ -3202,7 +3202,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="129"/>
|
||||
<source>Replace the current DNS server with your own. This will increase your privacy level.</source>
|
||||
<translation>Замініть DNS-сервер на Amnezia DNS. Це підвищить вашу рівень захищеності в інтернеті.</translation>
|
||||
<translation>Замініть DNS-сервер на AmneziaDNS. Це підвищить вашу рівень захищеності в інтернеті.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="138"/>
|
||||
@@ -3228,8 +3228,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
@@ -3340,7 +3338,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="78"/>
|
||||
<source>Sftp service</source>
|
||||
<source>SFTP service</source>
|
||||
<translation>Сервіс SFTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="162"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>ترتیب ک ھوگی،دوبارہ جوڑنےکی کوشش...</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -711,7 +711,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="95"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>شیڈو ساکس ترتیبات</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1057,13 +1057,13 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="140"/>
|
||||
<source>Github</source>
|
||||
<source>GitHub</source>
|
||||
<translation>گِٹ ہَب</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="147"/>
|
||||
<source>https://github.com/amnezia-vpn/amnezia-client</source>
|
||||
<translation></translation>
|
||||
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="158"/>
|
||||
@@ -1373,7 +1373,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="46"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>افتراضی سرور کا مخصوص DNS کو سپورٹ نہیں کرتا ہے</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1446,7 +1446,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="69"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>اس فعل کو فعال کرنے سے، ایپلیکیشن کے لاگ خود بخود محفوظ ہوجائیں گے۔ پہلے سے، لاگنگ کی فعالیت غیر فعال ہوتی ہے۔ اگر ایپلیکیشن میں کوئی خرابی ہو، تو لاگ کو بچانا فعال کریں.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1855,7 +1855,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="102"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>QR کوڈ</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2151,7 +2151,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>شیڈو ساکس کی ترتیبات کو محفوظ کریں</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2186,7 +2186,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>شیڈو ساکس کا اصل فارمیٹ</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2631,7 +2631,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="78"/>
|
||||
<source>Sftp service</source>
|
||||
<source>SFTP service</source>
|
||||
<translation>ایس ایف ٹی پی سروس</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2641,7 +2641,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<source>Unknown error</source>
|
||||
<translation>نامعلوم خامی</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2681,18 +2681,18 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>Ssh درخواست مسترد کر دی گئی</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH درخواست مسترد کر دی گئی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>Ssh درخواست میں خلل پڑ</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH درخواست میں خلل پڑ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>Ssh اندرونی خرابی</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>SSH اندرونی خرابی</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="28"/>
|
||||
@@ -2741,7 +2741,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="33"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation>ایس سی پی کی خرابی: عام ناکامی</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2756,7 +2756,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>شیڈو ساکس (ss-local) قابل عمل غائب</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2842,13 +2842,13 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="97"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>ایمنیزیا ڈی این ایس</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="98"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<translation>Sftp فائل شیئرنگ سروس</translation>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>SFTP فائل شیئرنگ سروس</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="104"/>
|
||||
@@ -2857,7 +2857,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="107"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>شیڈو ساکس - VPN ٹریفک کو ماسک کرتا ہے، جو اسے عام ویب ٹریفک جیسا بناتا ہے، لیکن اسے کچھ انتہائی سنسر والے علاقوں میں تجزیہ کے نظام کے ذریعے پہچانا جا سکتا ہے.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2947,8 +2947,8 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="124"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2 - جدید مستحکم پروٹوکول، دوسروں کے مقابلے میں تھوڑا تیز، سگنل ضائع ہونے کے بعد کنکشن بحال کرتا ہے۔</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2/IPsec - جدید مستحکم پروٹوکول، دوسروں کے مقابلے میں تھوڑا تیز، سگنل ضائع ہونے کے بعد کنکشن بحال کرتا ہے۔</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="127"/>
|
||||
@@ -2977,12 +2977,10 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>شیڈو ساکس، SOCKS5 پروٹوکول سے متاثر، AEAD سائفر کا استعمال کرتے ہوئے کنکشن کی حفاظت کرتا ہے۔ اگرچہ شیڈو ساکس کو سمجھدار اور شناخت کرنے کے لیے چیلنج کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن یہ معیاری HTTPS کنکشن سے مماثل نہیں ہے۔ تاہم، کچھ ٹریفک تجزیہ نظام اب بھی شیڈو ساکس کنکشن کا پتہ لگا سکتے ہیں۔ Amnezia میں محدود تعاون کی وجہ سے، AmneziaWG پروٹوکول استعمال کرنے کی سفارش کی جاتی ہے۔ * صرف ڈیسک ٹاپ پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * موبائل آلات پر بجلی کی عام کھپت * قابل ترتیب انکرپشن پروٹوکول * کچھ DPI سسٹمز کے ذریعے قابل شناخت * TCP نیٹ ورک پروٹوکول پر کام کرتا ہے.</translation>
|
||||
<translation>شیڈو ساکس، SOCKS5 پروٹوکول سے متاثر، AEAD سائفر کا استعمال کرتے ہوئے کنکشن کی حفاظت کرتا ہے۔ اگرچہ شیڈو ساکس کو سمجھدار اور شناخت کرنے کے لیے چیلنج کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن یہ معیاری HTTPS کنکشن سے مماثل نہیں ہے۔ تاہم، کچھ ٹریفک تجزیہ نظام اب بھی شیڈو ساکس کنکشن کا پتہ لگا سکتے ہیں۔ Amnezia میں محدود تعاون کی وجہ سے، AmneziaWG پروٹوکول استعمال کرنے کی سفارش کی جاتی ہے۔ * صرف ڈیسک ٹاپ پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * قابل ترتیب انکرپشن پروٹوکول * کچھ DPI سسٹمز کے ذریعے قابل شناخت * TCP نیٹ ورک پروٹوکول پر کام کرتا ہے.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="196"/>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
|
||||
<source>Settings updated successfully, Reconnnection...</source>
|
||||
<source>Settings updated successfully, reconnnection...</source>
|
||||
<translation>配置已更新, 重连中...</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -698,8 +698,8 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageProtocolShadowSocksSettings</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
|
||||
<source>ShadowSocks settings</source>
|
||||
<translation>ShadowSocks 配置</translation>
|
||||
<source>Shadowsocks settings</source>
|
||||
<translation>Shadowsocks 配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
|
||||
@@ -1039,13 +1039,13 @@ And if you don't like the app, all the more support it - the donation will
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
|
||||
<source>Github</source>
|
||||
<translation></translation>
|
||||
<source>GitHub</source>
|
||||
<translation>GitHub</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
|
||||
<source>https://github.com/amnezia-vpn/amnezia-client</source>
|
||||
<translation></translation>
|
||||
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="128"/>
|
||||
@@ -1323,8 +1323,8 @@ And if you don't like the app, all the more support it - the donation will
|
||||
<name>PageSettingsDns</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
|
||||
<source>Default server does not support custom dns</source>
|
||||
<translation>默认服务器不支持自定义 dns</translation>
|
||||
<source>Default server does not support custom DNS</source>
|
||||
<translation>默认服务器不支持自定义 DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
|
||||
@@ -1396,7 +1396,7 @@ And if you don't like the app, all the more support it - the donation will
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
|
||||
<source>Enabling this function will save application's logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>默认情况下,日志功能是禁用的。如果应用程序出现故障,则启用日志保存功能。</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1834,7 +1834,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
|
||||
<source>QR-code</source>
|
||||
<source>QR code</source>
|
||||
<translation>二维码</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2167,8 +2167,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
|
||||
<source>Save ShadowSocks config</source>
|
||||
<translation>保存 ShadowSocks 配置</translation>
|
||||
<source>Save Shadowsocks config</source>
|
||||
<translation>保存 Shadowsocks 配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
||||
@@ -2197,8 +2197,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
|
||||
<source>ShadowSocks native format</source>
|
||||
<translation>ShadowSocks原生格式</translation>
|
||||
<source>Shadowsocks native format</source>
|
||||
<translation>Shadowsocks原生格式</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
|
||||
@@ -2681,8 +2681,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../protocols/protocols_defs.cpp" line="77"/>
|
||||
<source>Sftp service</source>
|
||||
<translation>Sftp 服务</translation>
|
||||
<source>SFTP service</source>
|
||||
<translation>SFTP 服务</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="11"/>
|
||||
@@ -2691,7 +2691,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="12"/>
|
||||
<source>Unknown Error</source>
|
||||
<source>Unknown error</source>
|
||||
<translation>未知错误</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2731,18 +2731,18 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="24"/>
|
||||
<source>Ssh request was denied</source>
|
||||
<translation>ssh请求被拒绝</translation>
|
||||
<source>SSH request was denied</source>
|
||||
<translation>SSH请求被拒绝</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="25"/>
|
||||
<source>Ssh request was interrupted</source>
|
||||
<translation>ssh请求中断</translation>
|
||||
<source>SSH request was interrupted</source>
|
||||
<translation>SSH请求中断</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
<source>Ssh internal error</source>
|
||||
<translation>ssh内部错误</translation>
|
||||
<source>SSH internal error</source>
|
||||
<translation>SSH内部错误</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
@@ -2761,7 +2761,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
<source>Scp error: Generic failure</source>
|
||||
<source>SCP error: Generic failure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2887,8 +2887,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="40"/>
|
||||
<source>ShadowSocks (ss-local) executable missing</source>
|
||||
<translation>ShadowSocks (ss-local) 执行文件丢失</translation>
|
||||
<source>Shadowsocks (ss-local) executable missing</source>
|
||||
<translation>Shadowsocks (ss-local) 执行文件丢失</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="41"/>
|
||||
@@ -2947,12 +2947,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="91"/>
|
||||
<source>Amnezia DNS</source>
|
||||
<translation></translation>
|
||||
<source>AmneziaDNS</source>
|
||||
<translation>AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="92"/>
|
||||
<source>Sftp file sharing service</source>
|
||||
<source>SFTP file sharing service</source>
|
||||
<translation>SFTP文件共享服务</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2962,8 +2962,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="101"/>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>ShadowSocks - 掩盖VPN流量,使其类似于正常的网络流量,但在一些高度审查的地区可能会被分析系统识别.</translation>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
|
||||
<translation>Shadowsocks - 掩盖VPN流量,使其类似于正常的网络流量,但在一些高度审查的地区可能会被分析系统识别.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="217"/>
|
||||
@@ -3037,8 +3037,8 @@ WireGuard非常容易被阻挡,因为其独特的数据包签名。与一些
|
||||
通过UDP网络协议运行。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions.</source>
|
||||
<translation type="vanished">ShadowSocks - 混淆 VPN 流量,使其与正常的 Web 流量相似,但在一些审查力度高的地区可以被分析系统识别。</translation>
|
||||
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions.</source>
|
||||
<translation type="vanished">Shadowsocks - 混淆 VPN 流量,使其与正常的 Web 流量相似,但在一些审查力度高的地区可以被分析系统识别。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="104"/>
|
||||
@@ -3057,8 +3057,8 @@ WireGuard非常容易被阻挡,因为其独特的数据包签名。与一些
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2 - 现代稳定协议,相比其他协议较快一些,在信号丢失后恢复连接。</translation>
|
||||
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
|
||||
<translation>IKEv2/IPsec - 现代稳定协议,相比其他协议较快一些,在信号丢失后恢复连接。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
@@ -3098,16 +3098,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
<source>Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.
|
||||
|
||||
* Available in the AmneziaVPN only on desktop platforms
|
||||
* Normal power consumption on mobile devices
|
||||
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>Shadowsocks 受到 SOCKS5 协议的启发,使用 AEAD 密码保护连接。尽管 Shadowsocks 设计得谨慎且难以识别,但它与标准 HTTPS 连接并不相同。但是,某些流量分析系统可能仍会检测到 Shadowsocks 连接。由于Amnezia支持有限,建议使用AmneziaWG协议。
|
||||
|
||||
* 仅在桌面平台上的 AmneziaVPN 中可用
|
||||
* 移动设备的正常功耗
|
||||
|
||||
* 可配置的加密协议
|
||||
* 可以被某些 DPI 系统检测到
|
||||
* 通过 TCP 网络协议工作。</translation>
|
||||
|
||||
@@ -123,7 +123,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
void ConnectionController::onCurrentContainerUpdated()
|
||||
{
|
||||
if (m_isConnected || m_isConnectionInProgress) {
|
||||
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection..."));
|
||||
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnnection..."));
|
||||
openConnection();
|
||||
} else {
|
||||
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
|
||||
|
||||
@@ -123,6 +123,9 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
||||
} else if (container == DockerContainer::Sftp) {
|
||||
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||
containerConfig.insert(config_key::password, Utils::getRandomString(10));
|
||||
} else if (container == DockerContainer::Socks5Proxy) {
|
||||
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
|
||||
containerConfig.insert(config_key::password, Utils::getRandomString(10));
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
@@ -362,7 +365,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
if (containerInfo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
|
||||
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z0-9]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
|
||||
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
|
||||
if (containerAndPortMatch.hasMatch()) {
|
||||
QString name = containerAndPortMatch.captured(1);
|
||||
@@ -427,6 +430,20 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
|
||||
containerConfig.insert(config_key::userName, userName);
|
||||
containerConfig.insert(config_key::password, password);
|
||||
} else if (protocol == Proto::Socks5Proxy) {
|
||||
QString proxyConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||
protocols::socks5Proxy::proxyConfigPath, errorCode);
|
||||
|
||||
const static QRegularExpression usernameAndPasswordRegExp("users (\\w+):CL:(\\w+)");
|
||||
QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig);
|
||||
|
||||
if (usernameAndPasswordMatch.hasMatch()) {
|
||||
QString userName = usernameAndPasswordMatch.captured(1);
|
||||
QString password = usernameAndPasswordMatch.captured(2);
|
||||
|
||||
containerConfig.insert(config_key::userName, userName);
|
||||
containerConfig.insert(config_key::password, password);
|
||||
}
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
@@ -603,6 +620,10 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
|
||||
|
||||
int serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
|
||||
if (ContainerProps::containerService(container) == ServiceType::Other) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||
ServerCredentials serverCredentials =
|
||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace PageLoader
|
||||
PageServiceSftpSettings,
|
||||
PageServiceTorWebsiteSettings,
|
||||
PageServiceDnsSettings,
|
||||
PageServiceSocksProxySettings,
|
||||
|
||||
PageSetupWizardStart,
|
||||
PageSetupWizardCredentials,
|
||||
|
||||
@@ -281,7 +281,8 @@ ErrorCode ClientManagementModel::wgShow(const DockerContainer container, const S
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < peerList.size() && i < transferredDataList.size(); ++i) {
|
||||
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size(); ++i) {
|
||||
|
||||
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
|
||||
auto latestHandshake = getStrValue(latestHandshakeList[i]);
|
||||
auto serverBytesReceived = transferredData.front().trimmed();
|
||||
|
||||
@@ -86,6 +86,7 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
|
||||
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
|
||||
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
|
||||
case Proto::Sftp: return PageLoader::PageEnum::PageServiceSftpSettings;
|
||||
case Proto::Socks5Proxy: return PageLoader::PageEnum::PageServiceSocksProxySettings;
|
||||
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +548,8 @@ QStringList ServersModel::getAllInstalledServicesName(const int serverIndex)
|
||||
servicesName.append("SFTP");
|
||||
} else if (container == DockerContainer::TorWebSite) {
|
||||
servicesName.append("TOR");
|
||||
} else if (container == DockerContainer::Socks5Proxy) {
|
||||
servicesName.append("SOCKS5");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -615,15 +617,18 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
|
||||
{
|
||||
auto server = m_servers.at(m_defaultServerIndex).toObject();
|
||||
auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
|
||||
auto containerConfig = server.value(config_key::containers).toArray().at(defaultContainer).toObject();
|
||||
auto protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
|
||||
|
||||
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
|
||||
return !(protocolConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
|
||||
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|
||||
|| defaultContainer == DockerContainer::ShadowSocks) {
|
||||
return !(protocolConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
|
||||
auto containers = server.value(config_key::containers).toArray();
|
||||
for (auto i = 0; i < containers.size(); i++) {
|
||||
auto container = containers.at(i).toObject();
|
||||
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
|
||||
auto containerConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
|
||||
return !(containerConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
|
||||
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|
||||
|| defaultContainer == DockerContainer::ShadowSocks) {
|
||||
auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject();
|
||||
return !(containerConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
80
client/ui/models/services/socks5ProxyConfigModel.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "socks5ProxyConfigModel.h"
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
Socks5ProxyConfigModel::Socks5ProxyConfigModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int Socks5ProxyConfigModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Socks5ProxyConfigModel::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::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
|
||||
case Roles::UserNameRole: m_protocolConfig.insert(config_key::userName, value.toString()); break;
|
||||
case Roles::PasswordRole: m_protocolConfig.insert(config_key::password, value.toString()); break;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QList { role });
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant Socks5ProxyConfigModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString();
|
||||
case Roles::UserNameRole:
|
||||
return m_protocolConfig.value(config_key::userName).toString();
|
||||
case Roles::PasswordRole: return m_protocolConfig.value(config_key::password).toString();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void Socks5ProxyConfigModel::updateModel(const QJsonObject &config)
|
||||
{
|
||||
beginResetModel();
|
||||
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
||||
|
||||
m_fullConfig = config;
|
||||
QJsonObject protocolConfig = config.value(config_key::socks5proxy).toObject();
|
||||
|
||||
m_protocolConfig.insert(config_key::userName,
|
||||
protocolConfig.value(config_key::userName).toString());
|
||||
|
||||
m_protocolConfig.insert(config_key::password, protocolConfig.value(config_key::password).toString());
|
||||
|
||||
m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString());
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QJsonObject Socks5ProxyConfigModel::getConfig()
|
||||
{
|
||||
m_fullConfig.insert(config_key::socks5proxy, m_protocolConfig);
|
||||
return m_fullConfig;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> Socks5ProxyConfigModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[PortRole] = "port";
|
||||
roles[UserNameRole] = "username";
|
||||
roles[PasswordRole] = "password";
|
||||
|
||||
return roles;
|
||||
}
|
||||
40
client/ui/models/services/socks5ProxyConfigModel.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef SOCKS5PROXYCONFIGMODEL_H
|
||||
#define SOCKS5PROXYCONFIGMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
|
||||
class Socks5ProxyConfigModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
PortRole = Qt::UserRole + 1,
|
||||
UserNameRole,
|
||||
PasswordRole
|
||||
};
|
||||
|
||||
explicit Socks5ProxyConfigModel(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 // SOCKS5PROXYCONFIGMODEL_H
|
||||
@@ -92,7 +92,7 @@ PageType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("ShadowSocks settings")
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
|
||||
385
client/ui/qml/Pages2/PageServiceSocksProxySettings.qml
Normal file
@@ -0,0 +1,385 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
defaultActiveFocusItem: listview
|
||||
|
||||
Connections {
|
||||
target: InstallController
|
||||
|
||||
function onUpdateContainerFinished() {
|
||||
PageController.showNotificationMessage(qsTr("Settings updated successfully"))
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: focusItem
|
||||
KeyNavigation.tab: backButton
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
KeyNavigation.tab: listview
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: listview.implicitHeight
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
model: Socks5ProxyConfigModel
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
listview.currentItem.focusItemId.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
property alias focusItemId: hostLabel.rightButton
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
parentFlickable: fl
|
||||
KeyNavigation.tab: portLabel.rightButton
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
KeyNavigation.tab: usernameLabel.rightButton
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
KeyNavigation.tab: passwordLabel.eyeButton
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
eyeButton.KeyNavigation.tab: passwordLabel.rightButton
|
||||
rightButton.KeyNavigation.tab: changeSettingsButton
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: changeSettingsDrawer
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
onClosed: {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
expandedContent: ColumnLayout {
|
||||
property string tempPort: port
|
||||
property string tempUsername: username
|
||||
property string tempPassword: password
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 32
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
Connections {
|
||||
target: changeSettingsDrawer
|
||||
function onOpened() {
|
||||
if (!GC.isMobile()) {
|
||||
drawerFocusItem.forceActiveFocus()
|
||||
}
|
||||
tempPort = port
|
||||
tempUsername = username
|
||||
tempPassword = password
|
||||
}
|
||||
function onClosed() {
|
||||
port = tempPort
|
||||
username = tempUsername
|
||||
password = tempPassword
|
||||
portTextField.textFieldText = port
|
||||
usernameTextField.textFieldText = username
|
||||
passwordTextField.textFieldText = password
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: drawerFocusItem
|
||||
KeyNavigation.tab: portTextField.textField
|
||||
}
|
||||
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textFieldText: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textFieldText !== port) {
|
||||
port = textFieldText
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: usernameTextField.textField
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: usernameTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Username")
|
||||
textFieldPlaceholderText: "username"
|
||||
textFieldText: username
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textFieldText !== username) {
|
||||
username = textFieldText
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: passwordTextField.textField
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
property bool hidePassword: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Password")
|
||||
textFieldPlaceholderText: "password"
|
||||
textFieldText: password
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
||||
buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
||||
: ""
|
||||
|
||||
clickedFunc: function() {
|
||||
hidePassword = !hidePassword
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textFieldText !== password) {
|
||||
password = textFieldText
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: saveButton
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
Keys.onTabPressed: lastItemTabClicked(drawerFocusItem)
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
if (!portTextField.textField.acceptableInput) {
|
||||
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
if (usernameTextField.textFieldText && passwordTextField.textFieldText === "") {
|
||||
passwordTextField.errorText = qsTr("Password cannot be empty")
|
||||
return
|
||||
} else if (usernameTextField.textFieldText === "" && passwordTextField.textFieldText) {
|
||||
usernameTextField.errorText = qsTr("Username cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
|
||||
tempPort = portTextField.textFieldText
|
||||
tempUsername = usernameTextField.textFieldText
|
||||
tempPassword = passwordTextField.textFieldText
|
||||
changeSettingsDrawer.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changeSettingsButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
changeSettingsDrawer.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ PageType {
|
||||
id: githubButton
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Github")
|
||||
text: qsTr("GitHub")
|
||||
leftImageSource: "qrc:/images/controls/github.svg"
|
||||
|
||||
KeyNavigation.tab: websiteButton
|
||||
|
||||
@@ -43,7 +43,7 @@ PageType {
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isServerFromApi) {
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom dns"))
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ 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, " +
|
||||
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.")
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
|
||||
|
||||
defaultActiveFocusItem: focusItem
|
||||
|
||||
Item {
|
||||
@@ -103,6 +105,7 @@ PageType {
|
||||
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
PageController.goToPage(protocolPage);
|
||||
}
|
||||
@@ -124,7 +127,7 @@ PageType {
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
visible: root.isClearCacheVisible
|
||||
KeyNavigation.tab: removeButton
|
||||
|
||||
text: qsTr("Clear %1 profile").arg(ContainersModel.getProcessedContainerName())
|
||||
@@ -167,7 +170,7 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
visible: root.isClearCacheVisible
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
|
||||
@@ -99,7 +99,7 @@ PageType {
|
||||
Layout.fillWidth: true
|
||||
visible: SettingsController.isCameraPresent()
|
||||
|
||||
text: qsTr("QR-code")
|
||||
text: qsTr("QR code")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/qr-code.svg"
|
||||
|
||||
|
||||
@@ -261,6 +261,11 @@ PageType {
|
||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
||||
|
||||
clickedFunc: function() {
|
||||
if (!port.textField.acceptableInput) {
|
||||
port.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.install(dockerContainer, port.textFieldText, transportProtoSelector.currentIndex)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ PageType {
|
||||
}
|
||||
case PageShare.ConfigType.ShadowSocks: {
|
||||
ExportController.generateShadowSocksConfig()
|
||||
shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config")
|
||||
shareConnectionDrawer.configCaption = qsTr("Save Shadowsocks config")
|
||||
shareConnectionDrawer.configExtension = ".json"
|
||||
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
|
||||
break
|
||||
@@ -138,7 +138,7 @@ PageType {
|
||||
}
|
||||
QtObject {
|
||||
id: shadowSocksConnectionFormat
|
||||
property string name: qsTr("ShadowSocks native format")
|
||||
property string name: qsTr("Shadowsocks native format")
|
||||
property var type: PageShare.ConfigType.ShadowSocks
|
||||
}
|
||||
QtObject {
|
||||
|
||||
@@ -22,6 +22,7 @@ Options:
|
||||
-b, --build-platform <platform> The SDK platform used for building the Java code of the application
|
||||
By default, the latest available platform is used
|
||||
-m, --move Move the build result to the root of the build directory
|
||||
-f, --fdroid Build for F-Droid
|
||||
-h, --help Display this help
|
||||
|
||||
EOT
|
||||
@@ -29,7 +30,7 @@ EOT
|
||||
|
||||
BUILD_TYPE="release"
|
||||
|
||||
opts=$(getopt -l debug,aab,apk:,build-platform:,move,help -o "dua:b:mh" -- "$@")
|
||||
opts=$(getopt -l debug,aab,apk:,build-platform:,move,fdroid,help -o "dua:b:mfh" -- "$@")
|
||||
eval set -- "$opts"
|
||||
while true; do
|
||||
case "$1" in
|
||||
@@ -38,6 +39,7 @@ while true; do
|
||||
-a | --apk) ABIS=$2; shift 2;;
|
||||
-b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;;
|
||||
-m | --move) MOVE_RESULT=1; shift;;
|
||||
-f | --fdroid) FDROID=1; shift;;
|
||||
-h | --help) usage; exit 0;;
|
||||
--) shift; break;;
|
||||
esac
|
||||
@@ -143,6 +145,10 @@ $QT_HOST_PATH/bin/androiddeployqt \
|
||||
# run gradle
|
||||
gradle_opts=()
|
||||
|
||||
if [ -v FDROID ]; then
|
||||
BUILD_TYPE="fdroid"
|
||||
fi
|
||||
|
||||
if [ -v AAB ]; then
|
||||
gradle_opts+=(bundle"${BUILD_TYPE^}")
|
||||
fi
|
||||
@@ -167,10 +173,15 @@ if [[ -v CI || -v MOVE_RESULT ]]; then
|
||||
ABIS="x86;x86_64;armeabi-v7a;arm64-v8a"
|
||||
fi
|
||||
|
||||
suffix=$BUILD_TYPE
|
||||
if [ -v FDROID ]; then
|
||||
suffix+="-unsigned"
|
||||
fi
|
||||
|
||||
IFS=';' read -r -a abi_array <<< "$ABIS"
|
||||
for ABI in "${abi_array[@]}"
|
||||
do
|
||||
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/AmneziaVPN-$ABI-$BUILD_TYPE.apk \
|
||||
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/AmneziaVPN-$ABI-$suffix.apk \
|
||||
$PROJECT_DIR/deploy/build/
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
What's Changed:
|
||||
* Added XRay protocol support
|
||||
* Added app split tunneling
|
||||
* Added a notification to the status bar when VPN is running
|
||||
* Added a tile to the quick settings panel
|
||||
* Bug fixes and improvements
|
||||