mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54af96f4bf | |||
| 564d84e415 | |||
| 3842618aa6 | |||
| 992b6da7c9 | |||
| d1542d5f2a | |||
| cee6befa78 | |||
| 23be2749f7 | |||
| 89ed4d03a3 | |||
| 9ad3ace7e7 | |||
| d3715d00ae | |||
| c37662dbe2 | |||
| 768ca1e73d | |||
| a20516850c | |||
| 7a203868ec | |||
| 43c3ce9a6e | |||
| 369e08844f | |||
| 48a5452a65 | |||
| c2f9340db6 | |||
| a6508e642a | |||
| a3e73797c2 | |||
| df7bf204ea | |||
| e16243ff55 | |||
| e23cbe67ad | |||
| 7702f2f74c | |||
| b457ef9a3f | |||
| a28ed6a977 | |||
| 0c73682cfc | |||
| 7e380b6cfb | |||
| 63b5257986 | |||
| acc4485e81 | |||
| 2c44999a31 | |||
| e59a48f9f4 | |||
| b86356b0cc | |||
| f6d7552b58 | |||
| 5bd88ac2e9 | |||
| 94fa5b59f3 | |||
| 7169480999 | |||
| c44ce0d77c | |||
| 7fd71a8408 | |||
| 68db721089 | |||
| a180e12bdf | |||
| f3a4a1b1be | |||
| 6977a8ecbc | |||
| d00f64e6ad | |||
| d5b3da6ba3 | |||
| c245318339 | |||
| b3b0fec2e1 | |||
| 9d571a4c71 | |||
| f283858490 | |||
| 76fe203767 | |||
| b9a47f2f50 | |||
| 27cb17c640 | |||
| ef8fb89eb3 | |||
| f1b045f8a8 | |||
| 050066132b | |||
| 2a6e6a1e24 | |||
| 92689d084c | |||
| 00f314039d | |||
| fcb75e837d | |||
| 9fbea76b74 | |||
| b3ff120bcf | |||
| 9dea98f020 | |||
| c4701d4e7a | |||
| 48903ca3a1 | |||
| 0c9fd4aef4 | |||
| b2af2e46ac | |||
| efc76a0683 | |||
| c4a553c166 | |||
| 69a00b0252 | |||
| 4257c08b43 | |||
| c9e5b92f79 | |||
| 99818c2ad8 | |||
| 99e3afabad | |||
| d3339a7f3a | |||
| 678bfffe49 | |||
| 728b48044c | |||
| 7ccbfa48bc |
@@ -10,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
Build-Linux-Ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.6.2
|
||||
@@ -20,6 +20,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Install Qt'
|
||||
@@ -90,6 +92,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Get sources'
|
||||
@@ -156,6 +160,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
@@ -190,7 +196,7 @@ jobs:
|
||||
- name: 'Install go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22.1'
|
||||
go-version: '1.24'
|
||||
cache: false
|
||||
|
||||
- name: 'Setup gomobile'
|
||||
@@ -243,7 +249,7 @@ jobs:
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-MacOS:
|
||||
Build-MacOS-old:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
@@ -255,6 +261,78 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '15.4.0'
|
||||
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
host: 'mac'
|
||||
target: 'desktop'
|
||||
arch: 'clang_64'
|
||||
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||
dir: ${{ runner.temp }}
|
||||
setup-python: 'true'
|
||||
set-env: 'true'
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||
run: |
|
||||
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||
|
||||
- name: 'Get sources'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
fetch-depth: 10
|
||||
|
||||
- name: 'Setup ccache'
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
- name: 'Build project'
|
||||
run: |
|
||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
||||
bash deploy/build_macos.sh
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_old_installer
|
||||
path: AmneziaVPN.dmg
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_old_unpacked
|
||||
path: deploy/build/client/AmneziaVPN.app
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-MacOS:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.8.0
|
||||
QIF_VERSION: 4.8.1
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
@@ -324,6 +402,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Install desktop Qt'
|
||||
|
||||
@@ -20,6 +20,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Install desktop Qt'
|
||||
|
||||
@@ -1,64 +1,41 @@
|
||||
name: 'Upload a new version'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
RELEASE_VERSION:
|
||||
description: 'Release version (e.g. 1.2.3.4)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
Upload-S3:
|
||||
runs-on: ubuntu-latest
|
||||
name: upload
|
||||
steps:
|
||||
- name: Checkout CMakeLists.txt
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
ref: ${{ inputs.RELEASE_VERSION }}
|
||||
sparse-checkout: |
|
||||
CMakeLists.txt
|
||||
deploy/deploy_s3.sh
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Verify git tag
|
||||
run: |
|
||||
GIT_TAG=${{ github.ref_name }}
|
||||
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
||||
|
||||
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
|
||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
|
||||
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
||||
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
||||
else
|
||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
|
||||
echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download artifacts from the "${{ github.ref_name }}" tag
|
||||
uses: robinraju/release-downloader@v1.8
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
tag: ${{ github.ref_name }}
|
||||
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
|
||||
out-file-path: ${{ github.ref_name }}
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload beta version
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: contains(github.event.base_ref, 'dev')
|
||||
with:
|
||||
args: --include "AmneziaVPN*" --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: updates
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
||||
SOURCE_DIR: ${{ github.ref_name }}
|
||||
DEST_DIR: beta/${{ github.ref_name }}
|
||||
|
||||
- name: Upload stable version
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: contains(github.event.base_ref, 'master')
|
||||
with:
|
||||
args: --include "AmneziaVPN*" --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: updates
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
||||
SOURCE_DIR: ${{ github.ref_name }}
|
||||
DEST_DIR: stable/${{ github.ref_name }}
|
||||
- name: Send dist to S3
|
||||
run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_VERSION }}
|
||||
|
||||
+5
-1
@@ -133,4 +133,8 @@ client/3rd/ShadowSocks/ss_ios.xcconfig
|
||||
out/
|
||||
|
||||
# CMake files
|
||||
CMakeFiles/
|
||||
CMakeFiles/
|
||||
|
||||
ios-ne-build.sh
|
||||
macos-ne-build.sh
|
||||
macos-signed-build.sh
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
[submodule "client/3rd-prebuilt"]
|
||||
path = client/3rd-prebuilt
|
||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||
branch = feature/special-handshake
|
||||
[submodule "client/3rd/amneziawg-apple"]
|
||||
path = client/3rd/amneziawg-apple
|
||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.4.0
|
||||
project(${PROJECT} VERSION 4.8.7.2
|
||||
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 2077)
|
||||
set(APP_ANDROID_VERSION_CODE 2086)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
+10
-10
@@ -6,11 +6,11 @@
|
||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||
|
||||
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
|
||||
[](https://amnezia.org)
|
||||
|
||||
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
|
||||
> [!TIP]
|
||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
||||
@@ -30,7 +30,7 @@
|
||||
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
|
||||
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
|
||||
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
||||
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
|
||||
- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
|
||||
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
||||
|
||||
## Ссылки
|
||||
@@ -38,10 +38,10 @@
|
||||
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
||||
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
||||
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский)
|
||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси)
|
||||
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
|
||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский)
|
||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
|
||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
|
||||
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
|
||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
|
||||
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
|
||||
|
||||
## Технологии
|
||||
@@ -80,8 +80,8 @@ git submodule update --init --recursive
|
||||
Проверьте папку deploy для скриптов сборки.
|
||||
|
||||
### Как собрать iOS-приложение из исходного кода на MacOS
|
||||
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
|
||||
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
|
||||
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||
- MacOS
|
||||
- iOS
|
||||
- Модуль совместимости с Qt 5
|
||||
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||
```
|
||||
|
||||
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||
|
||||
Если сборка завершится с ошибкой:
|
||||
```
|
||||
|
||||
+1
-1
Submodule client/3rd-prebuilt updated: e555c78bcf...e3b6a33205
@@ -31,9 +31,8 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
||||
|
||||
if(IOS)
|
||||
set(PACKAGES ${PACKAGES} Multimedia)
|
||||
endif()
|
||||
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
||||
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(PACKAGES ${PACKAGES} Widgets)
|
||||
@@ -48,10 +47,6 @@ set(LIBS ${LIBS}
|
||||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(LIBS ${LIBS} Qt6::Widgets)
|
||||
endif()
|
||||
|
||||
+9
@@ -124,6 +124,15 @@ open class Wireguard : Protocol() {
|
||||
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
||||
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
||||
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
||||
configData.optStringOrNull("I1")?.let { setI1(it.toString()) }
|
||||
configData.optStringOrNull("I2")?.let { setI2(it.toString()) }
|
||||
configData.optStringOrNull("I3")?.let { setI3(it.toString()) }
|
||||
configData.optStringOrNull("I4")?.let { setI4(it.toString()) }
|
||||
configData.optStringOrNull("I5")?.let { setI5(it.toString()) }
|
||||
configData.optStringOrNull("J1")?.let { setJ1(it.toString()) }
|
||||
configData.optStringOrNull("J2")?.let { setJ2(it.toString()) }
|
||||
configData.optStringOrNull("J3")?.let { setJ3(it.toString()) }
|
||||
configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) }
|
||||
}
|
||||
|
||||
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
|
||||
+18
@@ -121,6 +121,15 @@ open class WireguardConfig protected constructor(
|
||||
internal var h2: Long? = null
|
||||
internal var h3: Long? = null
|
||||
internal var h4: Long? = null
|
||||
internal var i1: String? = null
|
||||
internal var i2: String? = null
|
||||
internal var i3: String? = null
|
||||
internal var i4: String? = null
|
||||
internal var i5: String? = null
|
||||
internal var j1: String? = null
|
||||
internal var j2: String? = null
|
||||
internal var j3: String? = null
|
||||
internal var itime: Int? = null
|
||||
|
||||
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
||||
|
||||
@@ -143,6 +152,15 @@ open class WireguardConfig protected constructor(
|
||||
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
||||
fun setI1(i1: String) = apply { this.i1 = i1 }
|
||||
fun setI2(i2: String) = apply { this.i2 = i2 }
|
||||
fun setI3(i3: String) = apply { this.i3 = i3 }
|
||||
fun setI4(i4: String) = apply { this.i4 = i4 }
|
||||
fun setI5(i5: String) = apply { this.i5 = i5 }
|
||||
fun setJ1(j1: String) = apply { this.j1 = j1 }
|
||||
fun setJ2(j2: String) = apply { this.j2 = j2 }
|
||||
fun setJ3(j3: String) = apply { this.j3 = j3 }
|
||||
fun setItime(itime: Int) = apply { this.itime = itime }
|
||||
|
||||
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
||||
}
|
||||
|
||||
+15
-1
@@ -76,8 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
|
||||
if(DEFINED DEPLOY)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||
)
|
||||
else()
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
|
||||
@@ -39,6 +39,15 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
|
||||
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
||||
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
||||
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
||||
jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
|
||||
jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
|
||||
jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
|
||||
jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
|
||||
jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
|
||||
jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1);
|
||||
jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2);
|
||||
jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3);
|
||||
jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout);
|
||||
jsonConfig[config_key::mtu] =
|
||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "core/networkUtilities.h"
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
|
||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
@@ -119,20 +120,14 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||
|
||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
#endif
|
||||
config.append("block-ipv6\n");
|
||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
|
||||
// no redirect-gateway
|
||||
// no redirect-gateway
|
||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
#endif
|
||||
config.append("block-ipv6\n");
|
||||
}
|
||||
@@ -169,7 +164,6 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("block-ipv6\n");
|
||||
|
||||
// remove block-outside-dns for all exported configs
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTemporaryFile>
|
||||
@@ -19,13 +20,17 @@
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent)
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
||||
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||
{
|
||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_serverConfigPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||
|
||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||
@@ -63,9 +68,31 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
return connData;
|
||||
}
|
||||
|
||||
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
|
||||
{
|
||||
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
|
||||
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
|
||||
|
||||
QList<QHostAddress> ips;
|
||||
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
const QString address_string { match.captured(1) };
|
||||
const QHostAddress address { address_string };
|
||||
if (address.isNull()) {
|
||||
qWarning() << "Couldn't recognize the ip address: " << address_string;
|
||||
} else {
|
||||
ips << address;
|
||||
}
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return connData;
|
||||
}
|
||||
|
||||
// Get list of already created clients (only IP addresses)
|
||||
QString nextIpNumber;
|
||||
{
|
||||
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
auto ips = getIpsFromConf(stdOut);
|
||||
|
||||
stdOut.replace("AllowedIPs = ", "");
|
||||
stdOut.replace("/32", "");
|
||||
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
||||
|
||||
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
||||
// and added there more IPs for route his itnernal networks, like:
|
||||
// ...
|
||||
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
|
||||
// ...
|
||||
// without this code - next IP would be 1 if last item in 'ips' has format above
|
||||
QStringList vpnIps;
|
||||
for (const auto &ip : ips) {
|
||||
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
|
||||
}
|
||||
ips = vpnIps;
|
||||
|
||||
// Calc next IP address
|
||||
if (ips.isEmpty()) {
|
||||
nextIpNumber = "2";
|
||||
QHostAddress nextIp = [&] {
|
||||
QHostAddress result;
|
||||
QHostAddress lastIp;
|
||||
if (ips.empty()) {
|
||||
lastIp.setAddress(containerConfig.value(m_protocolName)
|
||||
.toObject()
|
||||
.value(config_key::subnet_address)
|
||||
.toString(protocols::wireguard::defaultSubnetAddress));
|
||||
} else {
|
||||
int next = ips.last().split(".").last().toInt() + 1;
|
||||
if (next > 254) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
nextIpNumber = QString::number(next);
|
||||
lastIp = ips.last();
|
||||
}
|
||||
}
|
||||
|
||||
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
{
|
||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||
if (l.isEmpty()) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
|
||||
switch (lastOctet) {
|
||||
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
|
||||
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
||||
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
||||
}
|
||||
l.removeLast();
|
||||
l.append(nextIpNumber);
|
||||
|
||||
connData.clientIP = l.join(".");
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
connData.clientIP = nextIp.toString();
|
||||
|
||||
// Get keys
|
||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey.replace("\n", "");
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
@@ -161,10 +168,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||
.arg(m_serverConfigPath);
|
||||
|
||||
errorCode = m_serverController->runScript(
|
||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
credentials,
|
||||
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
|
||||
return connData;
|
||||
}
|
||||
@@ -173,8 +182,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
{
|
||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||
QString config =
|
||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
QString config = m_serverController->replaceVars(
|
||||
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QString &protocolConfigString)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
return protocolConfigString;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QString &protocolConfigString)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||
#define WIREGUARD_CONFIGURATOR_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent = nullptr);
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData
|
||||
{
|
||||
@@ -26,15 +27,18 @@ public:
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
|
||||
static ConnectionData genClientKeys();
|
||||
|
||||
private:
|
||||
QList<QHostAddress> getIpsFromConf(const QString &input);
|
||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
|
||||
|
||||
@@ -140,98 +140,83 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
{
|
||||
return {
|
||||
{ DockerContainer::OpenVpn,
|
||||
QObject::tr(
|
||||
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
||||
"It employs its unique security protocol, "
|
||||
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
||||
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
||||
"catering to a wide range of devices and operating systems. "
|
||||
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
||||
"which continually reinforces its security. "
|
||||
"With a strong balance of performance, security, and compatibility, "
|
||||
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
||||
"* Available in the AmneziaVPN across all platforms\n"
|
||||
"* Normal power consumption on mobile devices\n"
|
||||
"* Flexible customisation to suit user needs to work with different operating systems and devices\n"
|
||||
"* Recognised by DPI systems and therefore susceptible to blocking\n"
|
||||
"* Can operate over both TCP and UDP network protocols.") },
|
||||
QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
|
||||
"It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
|
||||
"and is continuously improved by the community due to its open-source nature. "
|
||||
"It provides a good balance between speed and security but is easily recognized by DPI systems, "
|
||||
"making it susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Normal battery consumption on mobile devices\n"
|
||||
"* Flexible customization for various devices and OS\n"
|
||||
"* Operates over both TCP and UDP protocols") },
|
||||
{ DockerContainer::ShadowSocks,
|
||||
QObject::tr("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.\n\n"
|
||||
"* Available in the AmneziaVPN only on desktop platforms\n"
|
||||
"* Configurable encryption protocol\n"
|
||||
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
|
||||
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
|
||||
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available in AmneziaVPN only on desktop platforms\n"
|
||||
"* Customizable encryption protocol\n"
|
||||
"* Detectable by some DPI systems\n"
|
||||
"* Works over TCP network protocol.") },
|
||||
"* Operates over TCP protocol\n") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
||||
"protecting against detection.\n\n"
|
||||
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
||||
"and the server.\n\n"
|
||||
"Cloak protects OpenVPN from detection. \n\n"
|
||||
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
||||
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
||||
"being detected\n\n"
|
||||
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
|
||||
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
|
||||
"invisible to analysis systems.\n\n"
|
||||
"* Available in the AmneziaVPN across all platforms\n"
|
||||
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
|
||||
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
|
||||
"\nThe Cloak plugin further protects the connection from DPI detection. "
|
||||
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
|
||||
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
|
||||
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* High power consumption on mobile devices\n"
|
||||
"* Flexible settings\n"
|
||||
"* Not recognised by detection systems\n"
|
||||
"* Works over TCP network protocol, 443 port.\n") },
|
||||
"* Flexible configuration options\n"
|
||||
"* Undetectable by DPI systems\n"
|
||||
"* Operates over TCP protocol on port 443") },
|
||||
{ DockerContainer::WireGuard,
|
||||
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
||||
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
||||
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
||||
"WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. "
|
||||
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
||||
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
||||
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
||||
"* Available in the AmneziaVPN across all platforms\n"
|
||||
"* Low power consumption\n"
|
||||
"* Minimum number of settings\n"
|
||||
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
|
||||
"* Works over UDP network protocol.") },
|
||||
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
|
||||
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
|
||||
"However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Low power consumption on mobile devices\n"
|
||||
"* Minimal configuration required\n"
|
||||
"* Easily detected by DPI systems (susceptible to blocking)\n"
|
||||
"* Operates over UDP protocol") },
|
||||
{ DockerContainer::Awg,
|
||||
QObject::tr("A modern iteration of the popular VPN protocol, "
|
||||
"AmneziaWG builds upon the foundation set by WireGuard, "
|
||||
"retaining its simplified architecture and high-performance capabilities across devices.\n"
|
||||
"While WireGuard is known for its efficiency, "
|
||||
"it had issues with being easily detected due to its distinct packet signatures. "
|
||||
"AmneziaWG solves this problem by using better obfuscation methods, "
|
||||
"making its traffic blend in with regular internet traffic.\n"
|
||||
"This means that AmneziaWG keeps the fast performance of the original "
|
||||
"while adding an extra layer of stealth, "
|
||||
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n"
|
||||
"* Available in the AmneziaVPN across all platforms\n"
|
||||
"* Low power consumption\n"
|
||||
"* Minimum number of settings\n"
|
||||
"* Not recognised by traffic analysis systems\n"
|
||||
"* Works over UDP network protocol.") },
|
||||
QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
|
||||
"combining simplified architecture with high performance across all devices. "
|
||||
"It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
|
||||
"making VPN traffic indistinguishable from regular internet traffic.\n"
|
||||
"\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Low battery consumption on mobile devices\n"
|
||||
"* Minimal settings required\n"
|
||||
"* Undetectable by traffic analysis systems (DPI)\n"
|
||||
"* Operates over UDP protocol") },
|
||||
{ DockerContainer::Xray,
|
||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
||||
"is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n"
|
||||
"It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, "
|
||||
"thus presenting an authentic TLS certificate and data. \n"
|
||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
||||
"legitimate sites without the need for specific configurations. \n"
|
||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. "
|
||||
"This makes REALITY a robust solution for maintaining internet freedom.")
|
||||
},
|
||||
QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
|
||||
"REALITY identifies censorship systems during the TLS handshake, "
|
||||
"redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
|
||||
"This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
|
||||
"\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
|
||||
"effectively protecting against DPI and other traffic analysis methods.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Resistant to active probing and DPI detection\n"
|
||||
"* No special configuration required to disguise traffic\n"
|
||||
"* Highly effective in heavily censored regions\n"
|
||||
"* Minimal battery consumption on devices\n"
|
||||
"* Operates over TCP protocol") },
|
||||
{ DockerContainer::Ipsec,
|
||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
||||
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
||||
"making it particularly adaptive in dynamic network environments. \n"
|
||||
"While it offers a blend of security, stability, and speed, "
|
||||
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n"
|
||||
"* Available in the AmneziaVPN only on Windows\n"
|
||||
"* Low power consumption, on mobile devices\n"
|
||||
"* Minimal configuration\n"
|
||||
"* Recognised by DPI analysis systems\n"
|
||||
"* Works over UDP network protocol, ports 500 and 4500.") },
|
||||
QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
|
||||
"It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
|
||||
"While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available in AmneziaVPN only on Windows\n"
|
||||
"* Low battery consumption on mobile devices\n"
|
||||
"* Minimal configuration required\n"
|
||||
"* Detectable by DPI analysis systems(easily blocked)\n"
|
||||
"* Operates over UDP protocol(ports 500 and 4500)") },
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace apiDefs
|
||||
AmneziaFreeV3,
|
||||
AmneziaPremiumV1,
|
||||
AmneziaPremiumV2,
|
||||
SelfHosted
|
||||
SelfHosted,
|
||||
ExternalPremium
|
||||
};
|
||||
|
||||
enum ConfigSource {
|
||||
@@ -21,12 +22,20 @@ namespace apiDefs
|
||||
namespace key
|
||||
{
|
||||
constexpr QLatin1String configVersion("config_version");
|
||||
constexpr QLatin1String apiEndpoint("api_endpoint");
|
||||
constexpr QLatin1String apiKey("api_key");
|
||||
constexpr QLatin1String description("description");
|
||||
constexpr QLatin1String name("name");
|
||||
constexpr QLatin1String protocol("protocol");
|
||||
|
||||
constexpr QLatin1String apiConfig("api_config");
|
||||
constexpr QLatin1String stackType("stack_type");
|
||||
constexpr QLatin1String serviceType("service_type");
|
||||
constexpr QLatin1String cliVersion("cli_version");
|
||||
|
||||
constexpr QLatin1String vpnKey("vpn_key");
|
||||
constexpr QLatin1String config("config");
|
||||
constexpr QLatin1String configs("configs");
|
||||
|
||||
constexpr QLatin1String installationUuid("installation_uuid");
|
||||
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
||||
@@ -43,6 +52,17 @@ namespace apiDefs
|
||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
||||
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
||||
constexpr QLatin1String issuedConfigs("issued_configs");
|
||||
|
||||
constexpr QLatin1String supportInfo("support_info");
|
||||
constexpr QLatin1String email("email");
|
||||
constexpr QLatin1String billingEmail("billing_email");
|
||||
constexpr QLatin1String website("website");
|
||||
constexpr QLatin1String websiteName("website_name");
|
||||
constexpr QLatin1String telegram("telegram");
|
||||
|
||||
constexpr QLatin1String id("id");
|
||||
constexpr QLatin1String orderId("order_id");
|
||||
constexpr QLatin1String migrationCode("migration_code");
|
||||
}
|
||||
|
||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
#include <QDateTime>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace
|
||||
{
|
||||
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
|
||||
|
||||
QString escapeUnicode(const QString &input)
|
||||
{
|
||||
QString output;
|
||||
for (QChar c : input) {
|
||||
if (c.unicode() < 0x20 || c.unicode() > 0x7E) {
|
||||
output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0'));
|
||||
} else {
|
||||
output += c;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
@@ -23,24 +41,34 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
|
||||
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
||||
|
||||
switch (configVersion) {
|
||||
case apiDefs::ConfigSource::Telegram: {
|
||||
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||
|
||||
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
|
||||
|
||||
if (apiEndpoint.contains(premiumV1Endpoint)) {
|
||||
return apiDefs::ConfigType::AmneziaPremiumV1;
|
||||
} else if (apiEndpoint.contains(freeV2Endpoint)) {
|
||||
return apiDefs::ConfigType::AmneziaFreeV2;
|
||||
}
|
||||
};
|
||||
case apiDefs::ConfigSource::AmneziaGateway: {
|
||||
constexpr QLatin1String stackPremium("prem");
|
||||
constexpr QLatin1String stackFree("free");
|
||||
|
||||
constexpr QLatin1String servicePremium("amnezia-premium");
|
||||
constexpr QLatin1String serviceFree("amnezia-free");
|
||||
constexpr QLatin1String serviceExternalPremium("external-premium");
|
||||
|
||||
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
||||
auto stackType = apiConfigObject.value(apiDefs::key::stackType).toString();
|
||||
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
|
||||
|
||||
if (serviceType == servicePremium || stackType == stackPremium) {
|
||||
if (serviceType == servicePremium) {
|
||||
return apiDefs::ConfigType::AmneziaPremiumV2;
|
||||
} else if (serviceType == serviceFree || stackType == stackFree) {
|
||||
} else if (serviceType == serviceFree) {
|
||||
return apiDefs::ConfigType::AmneziaFreeV3;
|
||||
} else if (serviceType == serviceExternalPremium) {
|
||||
return apiDefs::ConfigType::ExternalPremium;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
@@ -66,7 +94,11 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
return amnezia::ErrorCode::NoError;
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||
qDebug() << reply->error();
|
||||
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||
qDebug() << reply->error();
|
||||
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||
} else {
|
||||
QString err = reply->errorString();
|
||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
@@ -85,3 +117,48 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
qDebug() << "something went wrong";
|
||||
return amnezia::ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
|
||||
apiDefs::ConfigType::ExternalPremium };
|
||||
return premiumTypes.contains(getConfigType(serverConfigObject));
|
||||
}
|
||||
|
||||
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<QPair<QString, QVariant>> orderedFields;
|
||||
orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString()));
|
||||
orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString()));
|
||||
orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble()));
|
||||
orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString()));
|
||||
orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString()));
|
||||
orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString()));
|
||||
|
||||
QString vpnKeyStr = "{";
|
||||
for (int i = 0; i < orderedFields.size(); ++i) {
|
||||
const auto &pair = orderedFields[i];
|
||||
if (pair.second.typeId() == QMetaType::Type::QString) {
|
||||
vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\"";
|
||||
} else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) {
|
||||
vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1);
|
||||
}
|
||||
|
||||
if (i < orderedFields.size() - 1) {
|
||||
vpnKeyStr += ", ";
|
||||
}
|
||||
}
|
||||
vpnKeyStr += "}";
|
||||
|
||||
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
|
||||
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
|
||||
vpnKeyCompressed = vpnKeyCompressed.mid(4);
|
||||
|
||||
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
|
||||
|
||||
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
|
||||
}
|
||||
|
||||
@@ -13,10 +13,14 @@ namespace apiUtils
|
||||
|
||||
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
||||
|
||||
bool isPremiumServer(const QJsonObject &serverConfigObject);
|
||||
|
||||
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
|
||||
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
||||
|
||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
||||
|
||||
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
|
||||
}
|
||||
|
||||
#endif // APIUTILS_H
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "coreController.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QTranslator>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
@@ -20,6 +21,9 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
|
||||
initControllers();
|
||||
initSignalHandlers();
|
||||
|
||||
initAndroidController();
|
||||
initAppleController();
|
||||
|
||||
initNotificationHandler();
|
||||
|
||||
auto locale = m_settings->getAppLanguage();
|
||||
@@ -44,6 +48,9 @@ void CoreController::initModels()
|
||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||
|
||||
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
||||
|
||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||
|
||||
@@ -90,6 +97,9 @@ void CoreController::initModels()
|
||||
|
||||
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
|
||||
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
|
||||
|
||||
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
||||
}
|
||||
|
||||
void CoreController::initControllers()
|
||||
@@ -123,17 +133,24 @@ void CoreController::initControllers()
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
||||
|
||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||
|
||||
m_systemController.reset(new SystemController(m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
|
||||
m_apiSettingsController.reset(new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_settings));
|
||||
m_apiSettingsController.reset(
|
||||
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
|
||||
|
||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
||||
|
||||
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
||||
}
|
||||
|
||||
void CoreController::initAndroidController()
|
||||
@@ -206,6 +223,9 @@ void CoreController::initSignalHandlers()
|
||||
initAutoConnectHandler();
|
||||
initAmneziaDnsToggledHandler();
|
||||
initPrepareConfigHandler();
|
||||
initImportPremiumV2VpnKeyHandler();
|
||||
initShowMigrationDrawerHandler();
|
||||
initStrictKillSwitchHandler();
|
||||
}
|
||||
|
||||
void CoreController::initNotificationHandler()
|
||||
@@ -231,7 +251,23 @@ void CoreController::updateTranslator(const QLocale &locale)
|
||||
QCoreApplication::removeTranslator(m_translator.get());
|
||||
}
|
||||
|
||||
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
|
||||
QStringList availableTranslations;
|
||||
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
|
||||
while (it.hasNext()) {
|
||||
availableTranslations << it.next();
|
||||
}
|
||||
|
||||
// This code allow to load translation for the language only, without country code
|
||||
const QString lang = locale.name().split("_").first();
|
||||
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
|
||||
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
|
||||
for (const QString &translation : availableTranslations) {
|
||||
if (translation.contains(translationFilePrefix)) {
|
||||
strFileName = translation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_translator->load(strFileName)) {
|
||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||
m_settings->setAppLanguage(locale);
|
||||
@@ -332,6 +368,31 @@ void CoreController::initPrepareConfigHandler()
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initImportPremiumV2VpnKeyHandler()
|
||||
{
|
||||
connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
|
||||
m_importController->extractConfigFromData(vpnKey);
|
||||
m_importController->importConfig();
|
||||
|
||||
emit m_apiPremV1MigrationController->migrationFinished();
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initShowMigrationDrawerHandler()
|
||||
{
|
||||
QTimer::singleShot(1000, this, [this]() {
|
||||
if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) {
|
||||
m_apiPremV1MigrationController->showMigrationDrawer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initStrictKillSwitchHandler()
|
||||
{
|
||||
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
||||
&VpnConnection::onKillSwitchModeChanged);
|
||||
}
|
||||
|
||||
QSharedPointer<PageController> CoreController::pageController() const
|
||||
{
|
||||
return m_pageController;
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
#include "ui/controllers/api/apiConfigsController.h"
|
||||
#include "ui/controllers/api/apiSettingsController.h"
|
||||
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
||||
#include "ui/controllers/appSplitTunnelingController.h"
|
||||
#include "ui/controllers/allowedDnsController.h"
|
||||
#include "ui/controllers/connectionController.h"
|
||||
#include "ui/controllers/exportController.h"
|
||||
#include "ui/controllers/focusController.h"
|
||||
@@ -18,6 +20,7 @@
|
||||
#include "ui/controllers/sitesController.h"
|
||||
#include "ui/controllers/systemController.h"
|
||||
|
||||
#include "ui/models/allowed_dns_model.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
@@ -25,8 +28,9 @@
|
||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||
#endif
|
||||
#include "ui/models/api/apiAccountInfoModel.h"
|
||||
#include "ui/models/api/apiServicesModel.h"
|
||||
#include "ui/models/api/apiCountryModel.h"
|
||||
#include "ui/models/api/apiDevicesModel.h"
|
||||
#include "ui/models/api/apiServicesModel.h"
|
||||
#include "ui/models/appSplitTunnelingModel.h"
|
||||
#include "ui/models/clientManagementModel.h"
|
||||
#include "ui/models/protocols/awgConfigModel.h"
|
||||
@@ -79,6 +83,9 @@ private:
|
||||
void initAutoConnectHandler();
|
||||
void initAmneziaDnsToggledHandler();
|
||||
void initPrepareConfigHandler();
|
||||
void initImportPremiumV2VpnKeyHandler();
|
||||
void initShowMigrationDrawerHandler();
|
||||
void initStrictKillSwitchHandler();
|
||||
|
||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
@@ -101,9 +108,11 @@ private:
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
||||
|
||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
|
||||
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
||||
@@ -111,12 +120,14 @@ private:
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||
|
||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||
|
||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||
|
||||
@@ -7,14 +7,20 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
#include "QBlockCipher.h"
|
||||
#include "QRsa.h"
|
||||
|
||||
#include "amnezia_application.h"
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
#include "core/ipcclient.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
@@ -26,10 +32,21 @@ namespace
|
||||
constexpr char apiPayload[] = "api_payload";
|
||||
constexpr char keyPayload[] = "key_payload";
|
||||
}
|
||||
|
||||
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
||||
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
||||
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
||||
|
||||
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
|
||||
}
|
||||
|
||||
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
|
||||
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs)
|
||||
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||
const bool isStrictKillSwitchEnabled, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_gatewayEndpoint(gatewayEndpoint),
|
||||
m_isDevEnvironment(isDevEnvironment),
|
||||
m_requestTimeoutMsecs(requestTimeoutMsecs),
|
||||
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,6 +63,17 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
|
||||
|
||||
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
||||
|
||||
// bypass killSwitch exceptions for API-gateway
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_isStrictKillSwitchEnabled) {
|
||||
QString host = QUrl(request.url()).host();
|
||||
QString ip = NetworkUtilities::getIPAddress(host);
|
||||
if (!ip.isEmpty()) {
|
||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QNetworkReply *reply;
|
||||
reply = amnApp->networkManager()->get(request);
|
||||
|
||||
@@ -97,6 +125,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
|
||||
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
||||
|
||||
// bypass killSwitch exceptions for API-gateway
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_isStrictKillSwitchEnabled) {
|
||||
QString host = QUrl(request.url()).host();
|
||||
QString ip = NetworkUtilities::getIPAddress(host);
|
||||
if (!ip.isEmpty()) {
|
||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||
@@ -148,7 +187,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
|
||||
QByteArray encryptedResponseBody = reply->readAll();
|
||||
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
||||
request.setUrl(url);
|
||||
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
@@ -157,12 +196,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
||||
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
||||
encryptedResponseBody = nestedReply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
reply = nestedReply;
|
||||
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
sslErrors = nestedSslErrors;
|
||||
reply = nestedReply;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
||||
@@ -194,16 +233,16 @@ QStringList GatewayController::getProxyUrls()
|
||||
QList<QSslError> sslErrors;
|
||||
QNetworkReply *reply;
|
||||
|
||||
QStringList proxyStorageUrl;
|
||||
QStringList proxyStorageUrls;
|
||||
if (m_isDevEnvironment) {
|
||||
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
||||
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
|
||||
} else {
|
||||
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
||||
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
||||
}
|
||||
|
||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||
|
||||
for (const auto &proxyStorageUrl : proxyStorageUrl) {
|
||||
for (const auto &proxyStorageUrl : proxyStorageUrls) {
|
||||
request.setUrl(proxyStorageUrl);
|
||||
reply = amnApp->networkManager()->get(request);
|
||||
|
||||
@@ -212,62 +251,84 @@ QStringList GatewayController::getProxyUrls()
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
break;
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
reply->deleteLater();
|
||||
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
reply->deleteLater();
|
||||
EVP_PKEY *privateKey = nullptr;
|
||||
QByteArray responseBody;
|
||||
try {
|
||||
if (!m_isDevEnvironment) {
|
||||
QCryptographicHash hash(QCryptographicHash::Sha512);
|
||||
hash.addData(key);
|
||||
QByteArray hashResult = hash.result().toHex();
|
||||
|
||||
EVP_PKEY *privateKey = nullptr;
|
||||
QByteArray responseBody;
|
||||
try {
|
||||
if (!m_isDevEnvironment) {
|
||||
QCryptographicHash hash(QCryptographicHash::Sha512);
|
||||
hash.addData(key);
|
||||
QByteArray hashResult = hash.result().toHex();
|
||||
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
||||
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
||||
|
||||
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
||||
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
||||
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
||||
|
||||
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
||||
} else {
|
||||
responseBody = encryptedResponseBody;
|
||||
}
|
||||
} catch (...) {
|
||||
Utils::logException();
|
||||
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
||||
continue;
|
||||
}
|
||||
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
||||
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||
|
||||
QStringList endpoints;
|
||||
for (const auto &endpoint : endpointsArray) {
|
||||
endpoints.push_back(endpoint.toString());
|
||||
}
|
||||
return endpoints;
|
||||
} else {
|
||||
responseBody = encryptedResponseBody;
|
||||
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
||||
qDebug() << "go to the next storage endpoint";
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
} catch (...) {
|
||||
Utils::logException();
|
||||
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||
|
||||
QStringList endpoints;
|
||||
for (const auto &endpoint : endpointsArray) {
|
||||
endpoints.push_back(endpoint.toString());
|
||||
}
|
||||
return endpoints;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
||||
const QByteArray &iv, const QByteArray &salt)
|
||||
{
|
||||
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||
qDebug() << "Timeout occurred";
|
||||
qDebug() << "timeout occurred";
|
||||
qDebug() << reply->error();
|
||||
return true;
|
||||
} else if (responseBody.contains("html")) {
|
||||
qDebug() << "The response contains an html tag";
|
||||
qDebug() << "the response contains an html tag";
|
||||
return true;
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
|
||||
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|
||||
|| responseBody.contains(errorResponsePattern3)) {
|
||||
return false;
|
||||
} else {
|
||||
qDebug() << reply->error();
|
||||
return true;
|
||||
}
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||
if (responseBody.contains(updateRequestResponsePattern)) {
|
||||
return false;
|
||||
} else {
|
||||
qDebug() << reply->error();
|
||||
return true;
|
||||
}
|
||||
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||
qDebug() << reply->error();
|
||||
return true;
|
||||
} else if (checkEncryption) {
|
||||
try {
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||
} catch (...) {
|
||||
qDebug() << "Failed to decrypt the data";
|
||||
qDebug() << "failed to decrypt the data";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +349,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
||||
QByteArray responseBody;
|
||||
|
||||
for (const QString &proxyUrl : proxyUrls) {
|
||||
qDebug() << "Go to the next endpoint";
|
||||
qDebug() << "go to the next proxy endpoint";
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
reply = requestFunction(endpoint.arg(proxyUrl));
|
||||
|
||||
@@ -296,7 +357,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
|
||||
if (!replyProcessingFunction(reply, sslErrors)) {
|
||||
if (replyProcessingFunction(reply, sslErrors)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ class GatewayController : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr);
|
||||
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
||||
|
||||
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
|
||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||
@@ -30,6 +31,7 @@ private:
|
||||
int m_requestTimeoutMsecs;
|
||||
QString m_gatewayEndpoint;
|
||||
bool m_isDevEnvironment = false;
|
||||
bool m_isStrictKillSwitchEnabled = false;
|
||||
};
|
||||
|
||||
#endif // GATEWAYCONTROLLER_H
|
||||
|
||||
@@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
|
||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
@@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
return e;
|
||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
@@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
return e;
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
@@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
|
||||
errorCode = ErrorCode::NoError;
|
||||
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
||||
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -366,8 +366,27 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
||||
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
||||
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|
||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)))
|
||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
||||
// || (oldProtoConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1))
|
||||
// != newProtoConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1)
|
||||
// || (oldProtoConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2))
|
||||
// != newProtoConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2)
|
||||
// || (oldProtoConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3))
|
||||
// != newProtoConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3)
|
||||
// || (oldProtoConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4))
|
||||
// != newProtoConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4)
|
||||
// || (oldProtoConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5))
|
||||
// != newProtoConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5)
|
||||
// || (oldProtoConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1))
|
||||
// != newProtoConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1)
|
||||
// || (oldProtoConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2))
|
||||
// != newProtoConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2)
|
||||
// || (oldProtoConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3))
|
||||
// != newProtoConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3)
|
||||
// || (oldProtoConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout))
|
||||
// != newProtoConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout))
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -383,6 +402,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Xray) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -439,15 +465,22 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
errorCode =
|
||||
ErrorCode error =
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
if (errorCode)
|
||||
return errorCode;
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
return errorCode;
|
||||
if (stdOut.contains("doesn't work on cgroups v2"))
|
||||
return ErrorCode::ServerDockerOnCgroupsV2;
|
||||
if (stdOut.contains("cgroup mountpoint does not exist"))
|
||||
return ErrorCode::ServerCgroupMountpoint;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
@@ -624,6 +657,15 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
|
||||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||
// vars.append({ { "$SPECIAL_JUNK_1", amneziaWireguarConfig.value(config_key::specialJunk1).toString() } });
|
||||
// vars.append({ { "$SPECIAL_JUNK_2", amneziaWireguarConfig.value(config_key::specialJunk2).toString() } });
|
||||
// vars.append({ { "$SPECIAL_JUNK_3", amneziaWireguarConfig.value(config_key::specialJunk3).toString() } });
|
||||
// vars.append({ { "$SPECIAL_JUNK_4", amneziaWireguarConfig.value(config_key::specialJunk4).toString() } });
|
||||
// vars.append({ { "$SPECIAL_JUNK_5", amneziaWireguarConfig.value(config_key::specialJunk5).toString() } });
|
||||
// vars.append({ { "$CONTROLLED_JUNK_1", amneziaWireguarConfig.value(config_key::controlledJunk1).toString() } });
|
||||
// vars.append({ { "$CONTROLLED_JUNK_2", amneziaWireguarConfig.value(config_key::controlledJunk2).toString() } });
|
||||
// vars.append({ { "$CONTROLLED_JUNK_3", amneziaWireguarConfig.value(config_key::controlledJunk3).toString() } });
|
||||
// vars.append({ { "$SPECIAL_HANDSHAKE_TIMEOUT", amneziaWireguarConfig.value(config_key::specialHandshakeTimeout).toString() } });
|
||||
|
||||
// Socks5 proxy vars
|
||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||
@@ -709,7 +751,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||
|
||||
// TODO reimplement with netstat
|
||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
for (auto &port : fixedPorts) {
|
||||
script = script.append("|:%1").arg(port);
|
||||
}
|
||||
@@ -757,10 +799,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
|
||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
if (credentials.userName == "root") {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
@@ -774,8 +812,16 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (!stdOut.contains("sudo"))
|
||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||
return ErrorCode::ServerUserNotInSudo;
|
||||
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
|
||||
return ErrorCode::ServerUserDirectoryNotAccessible;
|
||||
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
||||
return ErrorCode::ServerUserNotAllowedInSudoers;
|
||||
if (stdOut.contains("password is required"))
|
||||
return ErrorCode::ServerUserPasswordRequired;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,12 @@ namespace amnezia
|
||||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
ServerSudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserDirectoryNotAccessible = 208,
|
||||
ServerUserNotAllowedInSudoers = 209,
|
||||
ServerUserPasswordRequired = 210,
|
||||
ServerDockerOnCgroupsV2 = 211,
|
||||
ServerCgroupMountpoint = 212,
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequestDeniedError = 300,
|
||||
@@ -111,6 +117,8 @@ namespace amnezia
|
||||
ApiServicesMissingError = 1107,
|
||||
ApiConfigLimitError = 1108,
|
||||
ApiNotFoundError = 1109,
|
||||
ApiMigrationError = 1110,
|
||||
ApiUpdateRequestError = 1111,
|
||||
|
||||
// QFile errors
|
||||
OpenError = 1200,
|
||||
|
||||
@@ -20,8 +20,14 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
||||
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
||||
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); break;
|
||||
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
||||
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
||||
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
@@ -68,6 +74,8 @@ QString errorString(ErrorCode code) {
|
||||
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
||||
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
||||
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
||||
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
|
||||
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
|
||||
|
||||
// QFile errors
|
||||
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <winsock.h>
|
||||
#include <QNetworkInterface>
|
||||
#include "qendian.h"
|
||||
#include <QSettings>
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <arpa/inet.h>
|
||||
@@ -185,6 +186,17 @@ int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool NetworkUtilities::checkIpv6Enabled() {
|
||||
#ifdef Q_OS_WIN
|
||||
QSettings RegHLM("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters",
|
||||
QSettings::NativeFormat);
|
||||
int ret = RegHLM.value("DisabledComponents", 0).toInt();
|
||||
qDebug() << "Check for Windows disabled IPv6 return " << ret;
|
||||
return (ret != 255);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
||||
const ULONG Flags,
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
||||
static bool checkIPv4Format(const QString &ip);
|
||||
static bool checkIpSubnetFormat(const QString &ip);
|
||||
static bool checkIpv6Enabled();
|
||||
static QString getGatewayAndIface();
|
||||
// Returns the Interface Index that could Route to dst
|
||||
static int AdapterIndexTo(const QHostAddress& dst);
|
||||
@@ -29,7 +30,6 @@ public:
|
||||
|
||||
static QString netMaskFromIpWithSubnet(const QString ip);
|
||||
static QString ipAddressFromIpWithSubnet(const QString ip);
|
||||
|
||||
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
||||
};
|
||||
|
||||
|
||||
@@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
logger.debug() << "Routing configuration failed for"
|
||||
<< logger.sensitive(ip.toString());
|
||||
logger.debug() << "Routing configuration failed for" << ip.toString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -371,6 +370,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||
|
||||
@@ -402,6 +404,34 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
||||
}
|
||||
|
||||
if (!obj.value("I1").isNull()) {
|
||||
config.m_specialJunk["I1"] = obj.value("I1").toString();
|
||||
}
|
||||
if (!obj.value("I2").isNull()) {
|
||||
config.m_specialJunk["I2"] = obj.value("I2").toString();
|
||||
}
|
||||
if (!obj.value("I3").isNull()) {
|
||||
config.m_specialJunk["I3"] = obj.value("I3").toString();
|
||||
}
|
||||
if (!obj.value("I4").isNull()) {
|
||||
config.m_specialJunk["I4"] = obj.value("I4").toString();
|
||||
}
|
||||
if (!obj.value("I5").isNull()) {
|
||||
config.m_specialJunk["I5"] = obj.value("I5").toString();
|
||||
}
|
||||
if (!obj.value("J1").isNull()) {
|
||||
config.m_controlledJunk["J1"] = obj.value("J1").toString();
|
||||
}
|
||||
if (!obj.value("J2").isNull()) {
|
||||
config.m_controlledJunk["J2"] = obj.value("J2").toString();
|
||||
}
|
||||
if (!obj.value("J3").isNull()) {
|
||||
config.m_controlledJunk["J3"] = obj.value("J3").toString();
|
||||
}
|
||||
if (!obj.value("Itime").isNull()) {
|
||||
config.m_specialHandshakeTimeout = obj.value("Itime").toString();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,13 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||
}
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
|
||||
QJsonArray jsAllowedDnsServers;
|
||||
for (const QString& i : m_allowedDnsServers) {
|
||||
jsAllowedDnsServers.append(QJsonValue(i));
|
||||
}
|
||||
json.insert("allowedDnsServers", jsAllowedDnsServers);
|
||||
|
||||
QJsonArray disabledApps;
|
||||
for (const QString& i : m_vpnDisabledApps) {
|
||||
disabledApps.append(QJsonValue(i));
|
||||
@@ -131,6 +138,16 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
||||
}
|
||||
|
||||
for (const QString& key : m_specialJunk.keys()) {
|
||||
out << key << " = " << m_specialJunk[key] << "\n";
|
||||
}
|
||||
for (const QString& key : m_controlledJunk.keys()) {
|
||||
out << key << " = " << m_controlledJunk[key] << "\n";
|
||||
}
|
||||
if (!m_specialHandshakeTimeout.isNull()) {
|
||||
out << "Itime = " << m_specialHandshakeTimeout << "\n";
|
||||
}
|
||||
|
||||
// If any extra config was provided, append it now.
|
||||
for (const QString& key : extra.keys()) {
|
||||
out << key << " = " << extra[key] << "\n";
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define INTERFACECONFIG_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include "ipaddress.h"
|
||||
@@ -37,6 +38,7 @@ class InterfaceConfig {
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
QStringList m_allowedDnsServers;
|
||||
bool m_killSwitchEnabled;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
QString m_installationId;
|
||||
@@ -51,6 +53,9 @@ class InterfaceConfig {
|
||||
QString m_responsePacketMagicHeader;
|
||||
QString m_underloadPacketMagicHeader;
|
||||
QString m_transportPacketMagicHeader;
|
||||
QMap<QString, QString> m_specialJunk;
|
||||
QMap<QString, QString> m_controlledJunk;
|
||||
QString m_specialHandshakeTimeout;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
QString toWgConf(
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 3H4C2.89543 3 2 3.89543 2 5V15C2 16.1046 2.89543 17 4 17H20C21.1046 17 22 16.1046 22 15V5C22 3.89543 21.1046 3 20 3Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 21H16" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 17V21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 522 B |
@@ -26,10 +26,22 @@ set_target_properties(networkextension PROPERTIES
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
|
||||
if(DEPLOY)
|
||||
set_target_properties(networkextension PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||
)
|
||||
else()
|
||||
set_target_properties(networkextension PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(networkextension PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
|
||||
@@ -38,7 +38,7 @@ LocalSocketController::LocalSocketController() {
|
||||
m_socket = new QLocalSocket(this);
|
||||
connect(m_socket, &QLocalSocket::connected, this,
|
||||
&LocalSocketController::daemonConnected);
|
||||
connect(m_socket, &QLocalSocket::disconnected, this,
|
||||
connect(m_socket, &QLocalSocket::disconnected, this,
|
||||
[&] { errorOccurred(QLocalSocket::PeerClosedError); });
|
||||
connect(m_socket, &QLocalSocket::errorOccurred, this,
|
||||
&LocalSocketController::errorOccurred);
|
||||
@@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
||||
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
||||
QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray();
|
||||
|
||||
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||
|
||||
@@ -134,7 +135,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
|
||||
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4.
|
||||
// Otherwise some OSes (Linux) try IPv6 forever and hang.
|
||||
// Otherwise some OSes (Linux) try IPv6 forever and hang.
|
||||
// https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193)
|
||||
// https://man7.org/linux/man-pages/man5/gai.conf.5.html
|
||||
json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it
|
||||
@@ -226,6 +227,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
json.insert("allowedDnsServers", allowedDns);
|
||||
|
||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||
|
||||
if (protocolName == amnezia::config_key::awg) {
|
||||
@@ -238,6 +241,15 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||
} else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
|
||||
@@ -246,7 +258,16 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) {
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialJunk5).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) {
|
||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||
@@ -256,6 +277,15 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||
}
|
||||
|
||||
write(json);
|
||||
|
||||
@@ -14,10 +14,15 @@ extension UIApplication {
|
||||
var keyWindows: [UIWindow] {
|
||||
connectedScenes
|
||||
.compactMap {
|
||||
guard let windowScene = $0 as? UIWindowScene else { return nil }
|
||||
if #available(iOS 15.0, *) {
|
||||
($0 as? UIWindowScene)?.keyWindow
|
||||
guard let keywindow = windowScene.keyWindow else {
|
||||
windowScene.windows.first?.makeKey()
|
||||
return windowScene.windows.first
|
||||
}
|
||||
return keywindow
|
||||
} else {
|
||||
($0 as? UIWindowScene)?.windows.first { $0.isKeyWindow }
|
||||
return windowScene.windows.first { $0.isKeyWindow }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ struct WGConfig: Decodable {
|
||||
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
||||
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
||||
let initPacketJunkSize, responsePacketJunkSize: String?
|
||||
let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String?
|
||||
let controlledJunk1, controlledJunk2, controlledJunk3: String?
|
||||
let specialHandshakeTimeout: String?
|
||||
let dns1: String
|
||||
let dns2: String
|
||||
let mtu: String
|
||||
@@ -24,6 +27,9 @@ struct WGConfig: Decodable {
|
||||
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
||||
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
||||
case specialJunk1 = "I1", specialJunk2 = "I2", specialJunk3 = "I3", specialJunk4 = "I4", specialJunk5 = "I5"
|
||||
case controlledJunk1 = "J1", controlledJunk2 = "J2", controlledJunk3 = "J3"
|
||||
case specialHandshakeTimeout = "Itime"
|
||||
case dns1
|
||||
case dns2
|
||||
case mtu
|
||||
@@ -51,7 +57,15 @@ struct WGConfig: Decodable {
|
||||
H2 = \(responsePacketMagicHeader!)
|
||||
H3 = \(underloadPacketMagicHeader!)
|
||||
H4 = \(transportPacketMagicHeader!)
|
||||
|
||||
I1 = \(specialJunk1!)
|
||||
I2 = \(specialJunk2!)
|
||||
I3 = \(specialJunk3!)
|
||||
I4 = \(specialJunk4!)
|
||||
I5 = \(specialJunk5!)
|
||||
J1 = \(controlledJunk1!)
|
||||
J2 = \(controlledJunk2!)
|
||||
J3 = \(controlledJunk3!)
|
||||
Itime = \(specialHandshakeTimeout!)
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ IPUtilsLinux::~IPUtilsLinux() {
|
||||
}
|
||||
|
||||
bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) {
|
||||
return addIP4AddressToDevice(config) && addIP6AddressToDevice(config);
|
||||
bool ret = addIP4AddressToDevice(config);
|
||||
addIP6AddressToDevice(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
|
||||
@@ -95,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr)
|
||||
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
@@ -136,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
|
||||
// Set ifr6 to the interface
|
||||
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
|
||||
if (ret && (errno != EEXIST)) {
|
||||
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr)
|
||||
logger.error() << "Failed to set IPv6: " << deviceAddr
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -455,9 +455,6 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers)
|
||||
|
||||
void LinuxFirewall::updateAllowNets(const QStringList& servers)
|
||||
{
|
||||
static QStringList existingServers {};
|
||||
|
||||
existingServers = servers;
|
||||
execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName));
|
||||
for (const QString& rule : getAllowRule(servers))
|
||||
execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule));
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||
|
||||
@@ -132,6 +134,16 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||
}
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
}
|
||||
for (const QString& key : config.m_controlledJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_controlledJunk.value(key) << "\n";
|
||||
}
|
||||
if (!config.m_specialHandshakeTimeout.isEmpty()) {
|
||||
out << "itime=" << config.m_specialHandshakeTimeout << "\n";
|
||||
}
|
||||
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
@@ -182,7 +194,7 @@ bool WireguardUtilsLinux::deleteInterface() {
|
||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
LinuxFirewall::uninstall();
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCAIFADDR, &ifr);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr)
|
||||
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) {
|
||||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr)
|
||||
logger.error() << "Failed to set IPv6: " << deviceAddr
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
|
||||
for (const IPAddress& prefix : m_exclusionRoutes) {
|
||||
if (prefix.address().protocol() == protocol) {
|
||||
logger.debug() << "Removing exclusion route to"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
<< prefix.toString();
|
||||
rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr);
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
|
||||
for (const IPAddress& prefix : m_exclusionRoutes) {
|
||||
if (prefix.address().protocol() == protocol) {
|
||||
logger.debug() << "Updating exclusion route to"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
<< prefix.toString();
|
||||
rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData());
|
||||
}
|
||||
}
|
||||
@@ -510,8 +510,7 @@ bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) {
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||
logger.debug() << "Adding exclusion route for"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
logger.debug() << "Adding exclusion route for" << prefix.toString();
|
||||
|
||||
if (m_exclusionRoutes.contains(prefix)) {
|
||||
logger.warning() << "Exclusion route already exists";
|
||||
@@ -536,8 +535,7 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||
logger.debug() << "Deleting exclusion route for"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
logger.debug() << "Deleting exclusion route for" << prefix.toString();
|
||||
|
||||
m_exclusionRoutes.removeAll(prefix);
|
||||
if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||
|
||||
@@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() {
|
||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
MacOSFirewall::uninstall();
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "logger.h"
|
||||
#include "platforms/windows/windowsutils.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
#define IPV6_ADDRESS_SIZE 16
|
||||
|
||||
// ID for the Firewall Sublayer
|
||||
@@ -180,16 +182,29 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
} \
|
||||
}
|
||||
|
||||
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
|
||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
||||
if (vpnAdapterIndex < 0)
|
||||
{
|
||||
IPAddress allv4("0.0.0.0/0");
|
||||
if (!blockTrafficTo(allv4, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
return false;
|
||||
}
|
||||
IPAddress allv6("::/0");
|
||||
if (!blockTrafficTo(allv6, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||
"Allow usage of VPN Adapter"));
|
||||
"Allow usage of VPN Adapter"));
|
||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||
"Allow all for AmneziaVPN.exe"));
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||
FW_OK(
|
||||
allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1"));
|
||||
FW_OK(allowLoopbackTraffic(MED_WEIGHT,
|
||||
"Allow Loopback traffic on device %1"));
|
||||
|
||||
logger.debug() << "Killswitch on! Rules:" << m_activeRules.length();
|
||||
return true;
|
||||
@@ -226,6 +241,37 @@ bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow unprotected traffic sent to the following address ranges.
|
||||
bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||
// Start the firewall transaction
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
disableKillSwitch();
|
||||
return false;
|
||||
}
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
disableKillSwitch();
|
||||
});
|
||||
|
||||
for (const QString& addr : ranges) {
|
||||
logger.debug() << "Allow killswitch exclude: " << addr;
|
||||
if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanup.dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
// Start the firewall transaction
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
@@ -262,12 +308,20 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString& dns : config.m_allowedDnsServers) {
|
||||
logger.debug() << "Allow DNS: " << dns;
|
||||
if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.m_excludedAddresses.empty()) {
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
logger.debug() << "excludedAddresses range: " << i;
|
||||
|
||||
if (!allowTrafficTo(i, HIGH_WEIGHT,
|
||||
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -313,37 +367,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
|
||||
}
|
||||
|
||||
bool WindowsFirewall::disableKillSwitch() {
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
return KillSwitch::instance()->disableKillSwitch();
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowAllTraffic() {
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
if (result != ERROR_SUCCESS) {
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
}
|
||||
});
|
||||
if (result != ERROR_SUCCESS) {
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& filterID : m_peerRules.values()) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
for (const auto& filterID : m_peerRules.values()) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
// Commit!
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
m_peerRules.clear();
|
||||
m_activeRules.clear();
|
||||
logger.debug() << "Firewall Disabled!";
|
||||
return true;
|
||||
// Commit!
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
m_peerRules.clear();
|
||||
m_activeRules.clear();
|
||||
logger.debug() << "Firewall Disabled!";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
|
||||
@@ -43,6 +43,8 @@ class WindowsFirewall final : public QObject {
|
||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||
bool disablePeerTraffic(const QString& pubkey);
|
||||
bool disableKillSwitch();
|
||||
bool allowAllTraffic();
|
||||
bool allowTrafficRange(const QStringList& ranges);
|
||||
|
||||
private:
|
||||
static bool initSublayer();
|
||||
|
||||
@@ -303,8 +303,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||
data->Age++;
|
||||
continue;
|
||||
}
|
||||
logger.debug() << "Capturing route to"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
logger.debug() << "Capturing route to" << prefix.toString();
|
||||
|
||||
// Clone the route and direct it into the VPN tunnel.
|
||||
data = new MIB_IPFORWARD_ROW2;
|
||||
@@ -354,8 +353,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug() << "Removing route capture for"
|
||||
<< logger.sensitive(i.key().toString());
|
||||
logger.debug() << "Removing route capture for" << i.key().toString();
|
||||
|
||||
// Otherwise, this route is no longer in use.
|
||||
DWORD result = DeleteIpForwardEntry2(data);
|
||||
@@ -368,8 +366,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||
}
|
||||
|
||||
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||
logger.debug() << "Adding exclusion route for"
|
||||
<< logger.sensitive(prefix.toString());
|
||||
logger.debug() << "Adding exclusion route for" << prefix.toString();
|
||||
|
||||
// Silently ignore non-routeable addresses.
|
||||
QHostAddress addr = prefix.address();
|
||||
@@ -437,7 +434,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||
|
||||
bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||
logger.debug() << "Deleting exclusion route for"
|
||||
<< logger.sensitive(prefix.address().toString());
|
||||
<< prefix.address().toString();
|
||||
|
||||
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
||||
if (data == nullptr) {
|
||||
@@ -447,7 +444,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||
DWORD result = DeleteIpForwardEntry2(data);
|
||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||
logger.error() << "Failed to delete route to"
|
||||
<< logger.sensitive(prefix.toString())
|
||||
<< prefix.toString()
|
||||
<< "result:" << result;
|
||||
}
|
||||
|
||||
@@ -465,7 +462,7 @@ void WindowsRouteMonitor::flushRouteTable(
|
||||
DWORD result = DeleteIpForwardEntry2(data);
|
||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||
logger.error() << "Failed to delete route to"
|
||||
<< logger.sensitive(i.key().toString())
|
||||
<< i.key().toString()
|
||||
<< "result:" << result;
|
||||
}
|
||||
delete data;
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "platforms/windows/windowscommons.h"
|
||||
#include "windowsdaemon.h"
|
||||
#include "windowsfirewall.h"
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
@@ -269,6 +267,13 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||
if (result == ERROR_OBJECT_ALREADY_EXISTS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case for ipv6 route with disabled ipv6
|
||||
if (prefix.address().protocol() == QAbstractSocket::IPv6Protocol
|
||||
&& result == ERROR_NOT_FOUND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result != NO_ERROR) {
|
||||
logger.error() << "Failed to create route to"
|
||||
<< prefix.toString()
|
||||
|
||||
@@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start()
|
||||
return lastError();
|
||||
}
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress(
|
||||
m_configData.value(amnezia::config_key::hostName).toString())));
|
||||
#endif
|
||||
|
||||
// Detect default gateway
|
||||
#ifdef Q_OS_MAC
|
||||
QProcess p;
|
||||
|
||||
@@ -76,6 +76,15 @@ namespace amnezia
|
||||
constexpr char responsePacketMagicHeader[] = "H2";
|
||||
constexpr char underloadPacketMagicHeader[] = "H3";
|
||||
constexpr char transportPacketMagicHeader[] = "H4";
|
||||
constexpr char specialJunk1[] = "I1";
|
||||
constexpr char specialJunk2[] = "I2";
|
||||
constexpr char specialJunk3[] = "I3";
|
||||
constexpr char specialJunk4[] = "I4";
|
||||
constexpr char specialJunk5[] = "I5";
|
||||
constexpr char controlledJunk1[] = "J1";
|
||||
constexpr char controlledJunk2[] = "J2";
|
||||
constexpr char controlledJunk3[] = "J3";
|
||||
constexpr char specialHandshakeTimeout[] = "Itime";
|
||||
|
||||
constexpr char openvpn[] = "openvpn";
|
||||
constexpr char wireguard[] = "wireguard";
|
||||
@@ -95,6 +104,8 @@ namespace amnezia
|
||||
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
||||
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
||||
|
||||
constexpr char allowedDnsServers[] = "allowedDnsServers";
|
||||
|
||||
constexpr char killSwitchOption[] = "killSwitchOption";
|
||||
|
||||
constexpr char crc[] = "crc";
|
||||
@@ -216,6 +227,15 @@ namespace amnezia
|
||||
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
|
||||
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
|
||||
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
||||
constexpr char defaultSpecialJunk1[] = "";
|
||||
constexpr char defaultSpecialJunk2[] = "";
|
||||
constexpr char defaultSpecialJunk3[] = "";
|
||||
constexpr char defaultSpecialJunk4[] = "";
|
||||
constexpr char defaultSpecialJunk5[] = "";
|
||||
constexpr char defaultControlledJunk1[] = "";
|
||||
constexpr char defaultControlledJunk2[] = "";
|
||||
constexpr char defaultControlledJunk3[] = "";
|
||||
constexpr char defaultSpecialHandshakeTimeout[] = "0";
|
||||
}
|
||||
|
||||
namespace socks5Proxy
|
||||
|
||||
+11
-1
@@ -129,6 +129,7 @@
|
||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
<file>ui/qml/Config/qmldir</file>
|
||||
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
||||
@@ -143,7 +144,9 @@
|
||||
<file>ui/qml/Controls2/DropDownType.qml</file>
|
||||
<file>ui/qml/Controls2/FlickableType.qml</file>
|
||||
<file>ui/qml/Controls2/Header2Type.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderType.qml</file>
|
||||
<file>ui/qml/Controls2/BaseHeaderType.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
|
||||
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
|
||||
<file>ui/qml/Controls2/ImageButtonType.qml</file>
|
||||
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
|
||||
@@ -199,6 +202,8 @@
|
||||
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
||||
@@ -229,6 +234,11 @@
|
||||
<file>ui/qml/Pages2/PageSettingsApiSupport.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
||||
<file>images/controls/monitor.svg</file>
|
||||
<file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file>
|
||||
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
|
||||
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/countriesFlags">
|
||||
<file>images/flagKit/ZW.svg</file>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "secure_qsettings.h"
|
||||
|
||||
#include "QAead.h"
|
||||
#include "QBlockCipher.h"
|
||||
#include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
|
||||
#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
|
||||
#include "utilities.h"
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
#include "keychain.h"
|
||||
#include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
|
||||
|
||||
class SecureQSettings : public QObject
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM amneziavpn/amnezia-wg:latest
|
||||
FROM marko1777/awg:latest
|
||||
|
||||
LABEL maintainer="AmneziaVPN"
|
||||
|
||||
@@ -10,7 +10,7 @@ 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
|
||||
|
||||
# Tune network
|
||||
# Tune network
|
||||
RUN echo -e " \n\
|
||||
fs.file-max = 51200 \n\
|
||||
\n\
|
||||
@@ -40,7 +40,8 @@ RUN echo -e " \n\
|
||||
echo -e " \n\
|
||||
* soft nofile 51200 \n\
|
||||
* hard nofile 51200 \n\
|
||||
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
|
||||
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
|
||||
|
||||
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
|
||||
CMD [ "" ]
|
||||
|
||||
|
||||
@@ -23,4 +23,5 @@ H1 = $INIT_PACKET_MAGIC_HEADER
|
||||
H2 = $RESPONSE_PACKET_MAGIC_HEADER
|
||||
H3 = $UNDERLOAD_PACKET_MAGIC_HEADER
|
||||
H4 = $TRANSPORT_PACKET_MAGIC_HEADER
|
||||
|
||||
EOF
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
CUR_USER=$(whoami);\
|
||||
groups $CUR_USER
|
||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
|
||||
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
|
||||
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
|
||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
||||
else pm="uname"; opt="-a";\
|
||||
fi;\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
|
||||
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
|
||||
sudo -K;\
|
||||
cd ~;\
|
||||
if [ "$CUR_USER" = "root" ] || ( groups "$CUR_USER" | grep -E '\<(sudo|wheel)\>' ); then \
|
||||
sudo -nu $CUR_USER $pm $opt > /dev/null; sudo -n $pm $opt > /dev/null;\
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CUR_USER=$(whoami);\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
|
||||
sudo mkdir -p $DOCKERFILE_FOLDER;\
|
||||
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
|
||||
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \
|
||||
|
||||
@@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled)
|
||||
setValue("Conf/killSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isStrictKillSwitchEnabled() const
|
||||
{
|
||||
return value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setStrictKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Settings::getInstallationUuid(const bool needCreate)
|
||||
{
|
||||
auto uuid = value("Conf/installationUuid", "").toString();
|
||||
@@ -548,3 +558,23 @@ void Settings::disableHomeAdLabel()
|
||||
{
|
||||
setValue("Conf/homeAdLabelVisible", false);
|
||||
}
|
||||
|
||||
bool Settings::isPremV1MigrationReminderActive()
|
||||
{
|
||||
return value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::disablePremV1MigrationReminder()
|
||||
{
|
||||
setValue("Conf/premV1MigrationReminderActive", false);
|
||||
}
|
||||
|
||||
QStringList Settings::allowedDnsServers() const
|
||||
{
|
||||
return value("Conf/allowedDnsServers").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||
{
|
||||
setValue("Conf/allowedDnsServers", servers);
|
||||
}
|
||||
|
||||
+13
-2
@@ -174,11 +174,12 @@ public:
|
||||
|
||||
QLocale getAppLanguage()
|
||||
{
|
||||
return value("Conf/appLanguage", QLocale()).toLocale();
|
||||
QString localeStr = m_settings.value("Conf/appLanguage").toString();
|
||||
return QLocale(localeStr);
|
||||
};
|
||||
void setAppLanguage(QLocale locale)
|
||||
{
|
||||
setValue("Conf/appLanguage", locale);
|
||||
setValue("Conf/appLanguage", locale.name());
|
||||
};
|
||||
|
||||
bool isScreenshotsEnabled() const
|
||||
@@ -213,6 +214,10 @@ public:
|
||||
|
||||
bool isKillSwitchEnabled() const;
|
||||
void setKillSwitchEnabled(bool enabled);
|
||||
|
||||
bool isStrictKillSwitchEnabled() const;
|
||||
void setStrictKillSwitchEnabled(bool enabled);
|
||||
|
||||
QString getInstallationUuid(const bool needCreate);
|
||||
|
||||
void resetGatewayEndpoint();
|
||||
@@ -225,6 +230,12 @@ public:
|
||||
bool isHomeAdLabelVisible();
|
||||
void disableHomeAdLabel();
|
||||
|
||||
bool isPremV1MigrationReminderActive();
|
||||
void disablePremV1MigrationReminder();
|
||||
|
||||
QStringList allowedDnsServers() const;
|
||||
void setAllowedDnsServers(const QStringList &servers);
|
||||
|
||||
signals:
|
||||
void saveLogsChanged(bool enabled);
|
||||
void screenshotsEnabledChanged(bool enabled);
|
||||
|
||||
+1423
-426
File diff suppressed because it is too large
Load Diff
+1443
-445
File diff suppressed because it is too large
Load Diff
+1419
-473
File diff suppressed because it is too large
Load Diff
+1433
-431
File diff suppressed because it is too large
Load Diff
+1485
-1123
File diff suppressed because it is too large
Load Diff
+1448
-450
File diff suppressed because it is too large
Load Diff
+1376
-447
File diff suppressed because it is too large
Load Diff
+1379
-482
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
TextArea {
|
||||
id: root
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding: 16
|
||||
leftPadding: 16
|
||||
|
||||
color: "#D7D8DB"
|
||||
selectionColor: "#412102"
|
||||
selectedTextColor: "#D7D8DB"
|
||||
placeholderTextColor: "#878B91"
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: contextMenu.open()
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textField
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#include "allowedDnsController.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "systemController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||
QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
|
||||
{
|
||||
}
|
||||
|
||||
void AllowedDnsController::addDns(QString ip)
|
||||
{
|
||||
if (ip.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||
emit errorOccurred(tr("The address does not look like a valid IP address"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_allowedDnsModel->addDns(ip)) {
|
||||
emit finished(tr("New DNS server added: %1").arg(ip));
|
||||
} else {
|
||||
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
|
||||
}
|
||||
}
|
||||
|
||||
void AllowedDnsController::removeDns(int index)
|
||||
{
|
||||
auto modelIndex = m_allowedDnsModel->index(index);
|
||||
auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString();
|
||||
m_allowedDnsModel->removeDns(modelIndex);
|
||||
|
||||
emit finished(tr("DNS server removed: %1").arg(ip));
|
||||
}
|
||||
|
||||
void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting)
|
||||
{
|
||||
QByteArray jsonData;
|
||||
if (!SystemController::readFile(fileName, jsonData)) {
|
||||
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
||||
if (jsonDocument.isNull()) {
|
||||
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDocument.isArray()) {
|
||||
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
auto jsonArray = jsonDocument.array();
|
||||
QStringList dnsServers;
|
||||
|
||||
for (auto jsonValue : jsonArray) {
|
||||
auto ip = jsonValue.toString();
|
||||
|
||||
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||
qDebug() << ip << " is not a valid IP address";
|
||||
continue;
|
||||
}
|
||||
|
||||
dnsServers.append(ip);
|
||||
}
|
||||
|
||||
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
|
||||
|
||||
emit finished(tr("Import completed"));
|
||||
}
|
||||
|
||||
void AllowedDnsController::exportDns(const QString &fileName)
|
||||
{
|
||||
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
|
||||
|
||||
QJsonArray jsonArray;
|
||||
|
||||
for (const auto &ip : dnsServers) {
|
||||
jsonArray.append(ip);
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument(jsonArray);
|
||||
QByteArray jsonData = jsonDocument.toJson();
|
||||
|
||||
SystemController::saveFile(fileName, jsonData);
|
||||
|
||||
emit finished(tr("Export completed"));
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef ALLOWEDDNSCONTROLLER_H
|
||||
#define ALLOWEDDNSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "settings.h"
|
||||
#include "ui/models/allowed_dns_model.h"
|
||||
|
||||
class AllowedDnsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void addDns(QString ip);
|
||||
void removeDns(int index);
|
||||
|
||||
void importDns(const QString &fileName, bool replaceExisting);
|
||||
void exportDns(const QString &fileName);
|
||||
|
||||
signals:
|
||||
void errorOccurred(const QString &errorMessage);
|
||||
void finished(const QString &message);
|
||||
|
||||
void saveFile(const QString &fileName, const QString &data);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||
};
|
||||
|
||||
#endif // ALLOWEDDNSCONTROLLER_H
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "apiConfigsController.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QClipboard>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "amnezia_application.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
@@ -19,7 +19,7 @@ namespace
|
||||
constexpr char cloak[] = "cloak";
|
||||
constexpr char awg[] = "awg";
|
||||
|
||||
constexpr char apiEdnpoint[] = "api_endpoint";
|
||||
constexpr char apiEndpoint[] = "api_endpoint";
|
||||
constexpr char accessToken[] = "api_key";
|
||||
constexpr char certificate[] = "certificate";
|
||||
constexpr char publicKey[] = "public_key";
|
||||
@@ -63,7 +63,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||
return false;
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
@@ -76,6 +77,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||
@@ -94,7 +96,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||
|
||||
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
@@ -107,6 +110,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||
@@ -140,7 +144,8 @@ void ApiConfigsController::copyVpnKeyToClipboard()
|
||||
|
||||
bool ApiConfigsController::fillAvailableServices()
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
@@ -171,7 +176,8 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||
return false;
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
auto userCountryCode = m_apiServicesModel->getCountryCode();
|
||||
@@ -184,6 +190,7 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||
apiPayload[configKey::serviceType] = serviceType;
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
|
||||
@@ -211,7 +218,8 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||
bool reloadServiceConfig)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
@@ -228,6 +236,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||
apiPayload[configKey::serviceType] = serviceType;
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
if (!newCountryCode.isEmpty()) {
|
||||
apiPayload[configKey::serverCountryCode] = newCountryCode;
|
||||
@@ -269,65 +278,50 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
|
||||
bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
|
||||
{
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
IosController::Instance()->requestInetAccess();
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(apiDefs::requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||
request.setUrl(endpoint);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
QString serviceProtocol = serverConfig.value(configKey::protocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString();
|
||||
apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString();
|
||||
|
||||
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
|
||||
|
||||
QNetworkReply *reply = amnApp->networkManager()->post(request, requestBody);
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
|
||||
|
||||
QEventLoop wait;
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
|
||||
QList<QSslError> sslErrors;
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
|
||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
reply->deleteLater();
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto apiResponseBody = reply->readAll();
|
||||
reply->deleteLater();
|
||||
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
|
||||
m_serversModel->editServer(serverConfig, serverIndex);
|
||||
emit updateServerFromApiFinished();
|
||||
return true;
|
||||
} else {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::deactivateDevice()
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
if (!apiUtils::isPremiumServer(serverConfigObject)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -340,6 +334,7 @@ bool ApiConfigsController::deactivateDevice()
|
||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
@@ -354,6 +349,45 @@ bool ApiConfigsController::deactivateDevice()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (!apiUtils::isPremiumServer(serverConfigObject)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
|
||||
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||
apiPayload[configKey::uuid] = uuid;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uuid == m_settings->getInstallationUuid(true)) {
|
||||
serverConfigObject.remove(config_key::containers);
|
||||
m_serversModel->editServer(serverConfigObject, serverIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isConfigValid()
|
||||
{
|
||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
@@ -369,7 +403,7 @@ bool ApiConfigsController::isConfigValid()
|
||||
return updateServiceFromGateway(serverIndex, "", "");
|
||||
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
|
||||
qDebug() << "attempt to update api config by expires_at event";
|
||||
if (configSource == apiDefs::ConfigSource::Telegram) {
|
||||
if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
return updateServiceFromGateway(serverIndex, "", "");
|
||||
} else {
|
||||
m_serversModel->removeApiConfig(serverIndex);
|
||||
@@ -449,6 +483,15 @@ void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPa
|
||||
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);
|
||||
containerConfig[config_key::specialJunk1] = protocolConfig.value(config_key::specialJunk1);
|
||||
containerConfig[config_key::specialJunk2] = protocolConfig.value(config_key::specialJunk2);
|
||||
containerConfig[config_key::specialJunk3] = protocolConfig.value(config_key::specialJunk3);
|
||||
containerConfig[config_key::specialJunk4] = protocolConfig.value(config_key::specialJunk4);
|
||||
containerConfig[config_key::specialJunk5] = protocolConfig.value(config_key::specialJunk5);
|
||||
containerConfig[config_key::controlledJunk1] = protocolConfig.value(config_key::controlledJunk1);
|
||||
containerConfig[config_key::controlledJunk2] = protocolConfig.value(config_key::controlledJunk2);
|
||||
containerConfig[config_key::controlledJunk3] = protocolConfig.value(config_key::controlledJunk3);
|
||||
containerConfig[config_key::specialHandshakeTimeout] = protocolConfig.value(config_key::specialHandshakeTimeout);
|
||||
container[containerName] = containerConfig;
|
||||
containers.replace(0, container);
|
||||
newServerConfig[config_key::containers] = containers;
|
||||
|
||||
@@ -31,6 +31,7 @@ public slots:
|
||||
bool reloadServiceConfig = false);
|
||||
bool updateServiceFromTelegram(const int serverIndex);
|
||||
bool deactivateDevice();
|
||||
bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
|
||||
|
||||
bool isConfigValid();
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "apiPremV1MigrationController.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/api/apiDefs.h"
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
|
||||
ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
bool ApiPremV1MigrationController::hasConfigsToMigration()
|
||||
{
|
||||
QJsonArray vpnKeys;
|
||||
|
||||
auto serversCount = m_serversModel->getServersCount();
|
||||
for (size_t i = 0; i < serversCount; i++) {
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(i);
|
||||
|
||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject);
|
||||
vpnKeys.append(vpnKey);
|
||||
}
|
||||
|
||||
if (!vpnKeys.isEmpty()) {
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload["configs"] = vpnKeys;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody);
|
||||
|
||||
auto migrationsStatus = QJsonDocument::fromJson(responseBody).object();
|
||||
for (const auto &migrationStatus : migrationsStatus) {
|
||||
if (migrationStatus == "not_found") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::getSubscriptionList(const QString &email)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = email;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
m_email = email;
|
||||
m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array();
|
||||
if (m_subscriptionsModel.isEmpty()) {
|
||||
emit noSubscriptionToMigrate();
|
||||
return;
|
||||
}
|
||||
|
||||
emit subscriptionsModelChanged();
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray ApiPremV1MigrationController::getSubscriptionModel()
|
||||
{
|
||||
return m_subscriptionsModel;
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex)
|
||||
{
|
||||
QEventLoop wait;
|
||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = m_email;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
m_subscriptionIndex = subscriptionIndex;
|
||||
emit otpSuccessfullySent();
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::migrate(const QString &migrationCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = m_email;
|
||||
apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString();
|
||||
apiPayload[apiDefs::key::migrationCode] = migrationCode;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
auto responseObject = QJsonDocument::fromJson(responseBody).object();
|
||||
QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString();
|
||||
|
||||
emit importPremiumV2VpnKey(premiumV2VpnKey);
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
bool ApiPremV1MigrationController::isPremV1MigrationReminderActive()
|
||||
{
|
||||
return m_settings->isPremV1MigrationReminderActive();
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::disablePremV1MigrationReminder()
|
||||
{
|
||||
m_settings->disablePremV1MigrationReminder();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef APIPREMV1MIGRATIONCONTROLLER_H
|
||||
#define APIPREMV1MIGRATIONCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiPremV1MigrationController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged)
|
||||
|
||||
public slots:
|
||||
bool hasConfigsToMigration();
|
||||
void getSubscriptionList(const QString &email);
|
||||
QJsonArray getSubscriptionModel();
|
||||
void sendMigrationCode(const int subscriptionIndex);
|
||||
void migrate(const QString &migrationCode);
|
||||
|
||||
bool isPremV1MigrationReminderActive();
|
||||
void disablePremV1MigrationReminder();
|
||||
|
||||
signals:
|
||||
void subscriptionsModelChanged();
|
||||
|
||||
void otpSuccessfullySent();
|
||||
|
||||
void importPremiumV2VpnKey(const QString &vpnKey);
|
||||
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
|
||||
void showMigrationDrawer();
|
||||
void migrationFinished();
|
||||
|
||||
void noSubscriptionToMigrate();
|
||||
|
||||
private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
QJsonArray m_subscriptionsModel;
|
||||
int m_subscriptionIndex;
|
||||
QString m_email;
|
||||
};
|
||||
|
||||
#endif // APIPREMV1MIGRATIONCONTROLLER_H
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -25,11 +26,13 @@ namespace
|
||||
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||
const QSharedPointer<ApiCountryModel> &apiCountryModel,
|
||||
const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_serversModel(serversModel),
|
||||
m_apiAccountInfoModel(apiAccountInfoModel),
|
||||
m_apiCountryModel(apiCountryModel),
|
||||
m_apiDevicesModel(apiDevicesModel),
|
||||
m_settings(settings)
|
||||
{
|
||||
}
|
||||
@@ -46,7 +49,8 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
wait.exec();
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
|
||||
@@ -57,15 +61,14 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
||||
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||
apiPayload[configKey::authData] = authData;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
|
||||
if (apiUtils::getConfigType(serverConfig) == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
|
||||
@@ -73,6 +76,7 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
|
||||
if (reload) {
|
||||
updateApiCountryModel();
|
||||
updateApiDevicesModel();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -83,3 +87,8 @@ void ApiSettingsController::updateApiCountryModel()
|
||||
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
||||
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||
}
|
||||
|
||||
void ApiSettingsController::updateApiDevicesModel()
|
||||
{
|
||||
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "ui/models/api/apiAccountInfoModel.h"
|
||||
#include "ui/models/api/apiCountryModel.h"
|
||||
#include "ui/models/api/apiDevicesModel.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiSettingsController : public QObject
|
||||
@@ -12,13 +13,14 @@ class ApiSettingsController : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||
const QSharedPointer<ApiCountryModel> &apiCountryModel, const std::shared_ptr<Settings> &settings,
|
||||
QObject *parent = nullptr);
|
||||
const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
|
||||
~ApiSettingsController();
|
||||
|
||||
public slots:
|
||||
bool getAccountInfo(bool reload);
|
||||
void updateApiCountryModel();
|
||||
void updateApiDevicesModel();
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
@@ -27,6 +29,7 @@ private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName)
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ void ExportController::generateAwgConfig(const QString &clientName)
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig()
|
||||
}
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ void ExportController::generateCloakConfig()
|
||||
nativeConfig.insert("ProxyMethod", "shadowsocks");
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ void ExportController::generateXrayConfig(const QString &clientName)
|
||||
}
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ namespace
|
||||
ConfigTypes checkConfigFormat(const QString &config)
|
||||
{
|
||||
const QString openVpnConfigPatternCli = "client";
|
||||
const QString openVpnConfigPatternProto1 = "proto tcp";
|
||||
const QString openVpnConfigPatternProto2 = "proto udp";
|
||||
const QString openVpnConfigPatternDriver1 = "dev tun";
|
||||
const QString openVpnConfigPatternDriver2 = "dev tap";
|
||||
|
||||
@@ -53,14 +51,13 @@ namespace
|
||||
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
|
||||
&& config.contains(amneziaConfigPatternPassword))) {
|
||||
return ConfigTypes::Amnezia;
|
||||
} else if (config.contains(openVpnConfigPatternCli)
|
||||
&& (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2))
|
||||
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||
return ConfigTypes::OpenVpn;
|
||||
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
|
||||
return ConfigTypes::WireGuard;
|
||||
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
||||
return ConfigTypes::Xray;
|
||||
} else if (config.contains(openVpnConfigPatternCli)
|
||||
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||
return ConfigTypes::OpenVpn;
|
||||
}
|
||||
return ConfigTypes::Invalid;
|
||||
}
|
||||
@@ -97,6 +94,8 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
|
||||
|
||||
bool ImportController::extractConfigFromData(QString data)
|
||||
{
|
||||
m_maliciousWarningText.clear();
|
||||
|
||||
QString config = data;
|
||||
QString prefix;
|
||||
QString errormsg;
|
||||
@@ -286,6 +285,15 @@ void ImportController::processNativeWireGuardConfig()
|
||||
clientProtocolConfig[config_key::responsePacketMagicHeader] = "2";
|
||||
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3";
|
||||
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4";
|
||||
clientProtocolConfig[config_key::specialJunk1] = "";
|
||||
clientProtocolConfig[config_key::specialJunk2] = "";
|
||||
clientProtocolConfig[config_key::specialJunk3] = "";
|
||||
clientProtocolConfig[config_key::specialJunk4] = "";
|
||||
clientProtocolConfig[config_key::specialJunk5] = "";
|
||||
clientProtocolConfig[config_key::controlledJunk1] = "";
|
||||
clientProtocolConfig[config_key::controlledJunk2] = "";
|
||||
clientProtocolConfig[config_key::controlledJunk3] = "";
|
||||
clientProtocolConfig[config_key::specialHandshakeTimeout] = "0";
|
||||
|
||||
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
|
||||
|
||||
@@ -345,7 +353,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
||||
arr.push_back(containers);
|
||||
|
||||
QString hostName;
|
||||
const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*");
|
||||
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
|
||||
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||
if (hostNameMatch.hasMatch()) {
|
||||
hostName = hostNameMatch.captured(1);
|
||||
@@ -444,7 +452,16 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
||||
&& !configMap.value(config_key::responsePacketJunkSize).isEmpty() && !configMap.value(config_key::initPacketMagicHeader).isEmpty()
|
||||
&& !configMap.value(config_key::responsePacketMagicHeader).isEmpty()
|
||||
&& !configMap.value(config_key::underloadPacketMagicHeader).isEmpty()
|
||||
&& !configMap.value(config_key::transportPacketMagicHeader).isEmpty()) {
|
||||
&& !configMap.value(config_key::transportPacketMagicHeader).isEmpty()
|
||||
&& !configMap.value(config_key::specialJunk1).isEmpty()
|
||||
&& !configMap.value(config_key::specialJunk2).isEmpty()
|
||||
&& !configMap.value(config_key::specialJunk3).isEmpty()
|
||||
&& !configMap.value(config_key::specialJunk4).isEmpty()
|
||||
&& !configMap.value(config_key::specialJunk5).isEmpty()
|
||||
&& !configMap.value(config_key::controlledJunk1).isEmpty()
|
||||
&& !configMap.value(config_key::controlledJunk2).isEmpty()
|
||||
&& !configMap.value(config_key::controlledJunk3).isEmpty()
|
||||
&& !configMap.value(config_key::specialHandshakeTimeout).isEmpty()) {
|
||||
lastConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
|
||||
lastConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
|
||||
lastConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
|
||||
@@ -454,6 +471,15 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
||||
lastConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
||||
lastConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
||||
lastConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
||||
lastConfig[config_key::specialJunk1] = configMap.value(config_key::specialJunk1);
|
||||
lastConfig[config_key::specialJunk2] = configMap.value(config_key::specialJunk2);
|
||||
lastConfig[config_key::specialJunk3] = configMap.value(config_key::specialJunk3);
|
||||
lastConfig[config_key::specialJunk4] = configMap.value(config_key::specialJunk4);
|
||||
lastConfig[config_key::specialJunk5] = configMap.value(config_key::specialJunk5);
|
||||
lastConfig[config_key::controlledJunk1] = configMap.value(config_key::controlledJunk1);
|
||||
lastConfig[config_key::controlledJunk2] = configMap.value(config_key::controlledJunk2);
|
||||
lastConfig[config_key::controlledJunk3] = configMap.value(config_key::controlledJunk3);
|
||||
lastConfig[config_key::specialHandshakeTimeout] = configMap.value(config_key::specialHandshakeTimeout);
|
||||
protocolName = "awg";
|
||||
m_configType = ConfigTypes::Awg;
|
||||
}
|
||||
@@ -661,29 +687,33 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
|
||||
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
|
||||
|
||||
QString protocolConfig =
|
||||
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
|
||||
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString();
|
||||
|
||||
const QRegularExpression regExp { "(\\w+-\\w+|\\w+)" };
|
||||
const size_t dangerousTagsMaxCount = 3;
|
||||
|
||||
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
|
||||
QStringList dangerousTags {
|
||||
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
|
||||
};
|
||||
|
||||
QStringList maliciousStrings;
|
||||
QStringList lines = protocolConfigJson.replace("\r", "").split("\n");
|
||||
for (const QString &l : lines) {
|
||||
QRegularExpressionMatch match = regExp.match(l);
|
||||
if (dangerousTags.contains(match.captured(0))) {
|
||||
maliciousStrings << l;
|
||||
QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts);
|
||||
|
||||
for (const QString &rawLine : lines) {
|
||||
QString line = rawLine.trimmed();
|
||||
|
||||
QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty);
|
||||
if (dangerousTags.contains(command, Qt::CaseInsensitive)) {
|
||||
maliciousStrings << rawLine;
|
||||
}
|
||||
}
|
||||
|
||||
if (maliciousStrings.size() >= dangerousTagsMaxCount) {
|
||||
m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:");
|
||||
m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
|
||||
"scripts, so only add it if you fully trust the provider of this config. ");
|
||||
|
||||
if (!maliciousStrings.isEmpty()) {
|
||||
m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:"));
|
||||
for (const auto &string : maliciousStrings) {
|
||||
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
|
||||
}
|
||||
|
||||
@@ -108,6 +108,18 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
||||
containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader;
|
||||
containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader;
|
||||
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
|
||||
|
||||
// TODO:
|
||||
// containerConfig[config_key::specialJunk1] = specialJunk1;
|
||||
// containerConfig[config_key::specialJunk2] = specialJunk2;
|
||||
// containerConfig[config_key::specialJunk3] = specialJunk3;
|
||||
// containerConfig[config_key::specialJunk4] = specialJunk4;
|
||||
// containerConfig[config_key::specialJunk5] = specialJunk5;
|
||||
// containerConfig[config_key::controlledJunk1] = controlledJunk1;
|
||||
// containerConfig[config_key::controlledJunk2] = controlledJunk2;
|
||||
// containerConfig[config_key::controlledJunk3] = controlledJunk3;
|
||||
// containerConfig[config_key::specialHandshakeTimeout] = specialHandshakeTimeout;
|
||||
|
||||
} else if (container == DockerContainer::Sftp) {
|
||||
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||
containerConfig.insert(config_key::password, Utils::getRandomString(16));
|
||||
@@ -363,7 +375,8 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
|
||||
QJsonObject config;
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
const auto &protocols = ContainerProps::protocolsForContainer(container);
|
||||
for (const auto &protocol : protocols) {
|
||||
QJsonObject containerConfig;
|
||||
if (protocol == mainProto) {
|
||||
containerConfig.insert(config_key::port, port);
|
||||
@@ -387,6 +400,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
}
|
||||
}
|
||||
|
||||
containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24");
|
||||
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
|
||||
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
|
||||
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
|
||||
@@ -398,6 +412,35 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||
containerConfig[config_key::transportPacketMagicHeader] =
|
||||
serverConfigMap.value(config_key::transportPacketMagicHeader);
|
||||
containerConfig[config_key::specialJunk1] = serverConfigMap.value(config_key::specialJunk1);
|
||||
containerConfig[config_key::specialJunk2] = serverConfigMap.value(config_key::specialJunk2);
|
||||
containerConfig[config_key::specialJunk3] = serverConfigMap.value(config_key::specialJunk3);
|
||||
containerConfig[config_key::specialJunk4] = serverConfigMap.value(config_key::specialJunk4);
|
||||
containerConfig[config_key::specialJunk5] = serverConfigMap.value(config_key::specialJunk5);
|
||||
containerConfig[config_key::controlledJunk1] = serverConfigMap.value(config_key::controlledJunk1);
|
||||
containerConfig[config_key::controlledJunk2] = serverConfigMap.value(config_key::controlledJunk2);
|
||||
containerConfig[config_key::controlledJunk3] = serverConfigMap.value(config_key::controlledJunk3);
|
||||
containerConfig[config_key::specialHandshakeTimeout] = serverConfigMap.value(config_key::specialHandshakeTimeout);
|
||||
|
||||
|
||||
} else if (protocol == Proto::WireGuard) {
|
||||
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||
protocols::wireguard::serverConfigPath, errorCode);
|
||||
|
||||
QMap<QString, QString> serverConfigMap;
|
||||
auto serverConfigLines = serverConfig.split("\n");
|
||||
for (auto &line : serverConfigLines) {
|
||||
auto trimmedLine = line.trimmed();
|
||||
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||
continue;
|
||||
} else {
|
||||
QStringList parts = trimmedLine.split(" = ");
|
||||
if (parts.count() == 2) {
|
||||
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||
}
|
||||
}
|
||||
}
|
||||
containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24");
|
||||
} else if (protocol == Proto::Sftp) {
|
||||
stdOut.clear();
|
||||
script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name);
|
||||
@@ -432,6 +475,51 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
containerConfig.insert(config_key::userName, userName);
|
||||
containerConfig.insert(config_key::password, password);
|
||||
}
|
||||
} else if (protocol == Proto::Xray) {
|
||||
QString currentConfig = serverController->getTextFileFromContainer(
|
||||
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
|
||||
qDebug() << doc;
|
||||
if (doc.isNull() || !doc.isObject()) {
|
||||
logger.error() << "Failed to parse server config JSON";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
QJsonObject serverConfig = doc.object();
|
||||
|
||||
if (!serverConfig.contains("inbounds")) {
|
||||
logger.error() << "Server config missing 'inbounds' field";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonArray inbounds = serverConfig["inbounds"].toArray();
|
||||
if (inbounds.isEmpty()) {
|
||||
logger.error() << "Server config has empty 'inbounds' array";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject inbound = inbounds[0].toObject();
|
||||
if (!inbound.contains("streamSettings")) {
|
||||
logger.error() << "Inbound missing 'streamSettings' field";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject streamSettings = inbound["streamSettings"].toObject();
|
||||
QJsonObject realitySettings = streamSettings["realitySettings"].toObject();
|
||||
if (!realitySettings.contains("serverNames")) {
|
||||
logger.error() << "Settings missing 'clients' field";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QString siteName = realitySettings["serverNames"][0].toString();
|
||||
qDebug() << siteName;
|
||||
|
||||
containerConfig.insert(config_key::site, siteName);
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
|
||||
@@ -31,12 +31,15 @@ namespace PageLoader
|
||||
PageSettingsLogging,
|
||||
PageSettingsSplitTunneling,
|
||||
PageSettingsAppSplitTunneling,
|
||||
PageSettingsKillSwitch,
|
||||
PageSettingsApiServerInfo,
|
||||
PageSettingsApiAvailableCountries,
|
||||
PageSettingsApiSupport,
|
||||
PageSettingsApiInstructions,
|
||||
PageSettingsApiNativeConfigs,
|
||||
|
||||
PageSettingsApiDevices,
|
||||
PageSettingsKillSwitchExceptions,
|
||||
|
||||
PageServiceSftpSettings,
|
||||
PageServiceTorWebsiteSettings,
|
||||
PageServiceDnsSettings,
|
||||
|
||||
@@ -126,7 +126,13 @@ void SettingsController::clearLogs()
|
||||
|
||||
void SettingsController::backupAppConfig(const QString &fileName)
|
||||
{
|
||||
SystemController::saveFile(fileName, m_settings->backupAppConfig());
|
||||
QByteArray data = m_settings->backupAppConfig();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
QJsonObject config = doc.object();
|
||||
|
||||
config["Conf/autoStart"] = Autostart::isAutostart();
|
||||
|
||||
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||
}
|
||||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
@@ -140,9 +146,30 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
{
|
||||
bool ok = m_settings->restoreAppConfig(data);
|
||||
if (ok) {
|
||||
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
|
||||
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
|
||||
bool autoStart = false;
|
||||
if (newConfigData.contains("Conf/autoStart")) {
|
||||
autoStart = newConfigData["Conf/autoStart"].toBool();
|
||||
}
|
||||
toggleAutoStart(autoStart);
|
||||
#endif
|
||||
m_serversModel->resetModel();
|
||||
m_languageModel->changeLanguage(
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
||||
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
|
||||
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
||||
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
||||
#endif
|
||||
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
|
||||
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||
|
||||
emit restoreBackupFinished();
|
||||
} else {
|
||||
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
||||
@@ -167,6 +194,8 @@ void SettingsController::clearSettings()
|
||||
m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps);
|
||||
m_appSplitTunnelingModel->toggleSplitTunneling(false);
|
||||
|
||||
toggleAutoStart(false);
|
||||
|
||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
@@ -245,6 +274,23 @@ bool SettingsController::isKillSwitchEnabled()
|
||||
void SettingsController::toggleKillSwitch(bool enable)
|
||||
{
|
||||
m_settings->setKillSwitchEnabled(enable);
|
||||
emit killSwitchEnabledChanged();
|
||||
if (enable == false) {
|
||||
emit strictKillSwitchEnabledChanged(false);
|
||||
} else {
|
||||
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
bool SettingsController::isStrictKillSwitchEnabled()
|
||||
{
|
||||
return m_settings->isStrictKillSwitchEnabled();
|
||||
}
|
||||
|
||||
void SettingsController::toggleStrictKillSwitch(bool enable)
|
||||
{
|
||||
m_settings->setStrictKillSwitchEnabled(enable);
|
||||
emit strictKillSwitchEnabledChanged(enable);
|
||||
}
|
||||
|
||||
bool SettingsController::isNotificationPermissionGranted()
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
||||
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
|
||||
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
|
||||
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
|
||||
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
|
||||
|
||||
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
|
||||
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
|
||||
@@ -75,6 +77,9 @@ public slots:
|
||||
bool isKillSwitchEnabled();
|
||||
void toggleKillSwitch(bool enable);
|
||||
|
||||
bool isStrictKillSwitchEnabled();
|
||||
void toggleStrictKillSwitch(bool enable);
|
||||
|
||||
bool isNotificationPermissionGranted();
|
||||
void requestNotificationPermission();
|
||||
|
||||
@@ -98,6 +103,8 @@ signals:
|
||||
void primaryDnsChanged();
|
||||
void secondaryDnsChanged();
|
||||
void loggingStateChanged();
|
||||
void killSwitchEnabledChanged();
|
||||
void strictKillSwitchEnabledChanged(bool enabled);
|
||||
|
||||
void restoreBackupFinished();
|
||||
void changeSettingsFinished(const QString &finishedMessage);
|
||||
|
||||
@@ -44,7 +44,6 @@ void SitesController::addSite(QString hostname)
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
}
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
||||
};
|
||||
|
||||
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
|
||||
@@ -75,7 +74,6 @@ void SitesController::removeSite(int index)
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
||||
|
||||
emit finished(tr("Site removed: %1").arg(hostname));
|
||||
}
|
||||
@@ -124,7 +122,6 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||
m_sitesModel->addSites(sites, replaceExisting);
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
||||
|
||||
emit finished(tr("Import completed"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "allowed_dns_model.h"
|
||||
|
||||
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QAbstractListModel(parent), m_settings(settings)
|
||||
{
|
||||
fillDnsServers();
|
||||
}
|
||||
|
||||
int AllowedDnsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_dnsServers.size();
|
||||
}
|
||||
|
||||
QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case IpRole:
|
||||
return m_dnsServers.at(index.row());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool AllowedDnsModel::addDns(const QString &ip)
|
||||
{
|
||||
if (m_dnsServers.contains(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_dnsServers.append(ip);
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
if (replaceExisting) {
|
||||
m_dnsServers.clear();
|
||||
}
|
||||
|
||||
for (const QString &ip : dnsServers) {
|
||||
if (!m_dnsServers.contains(ip)) {
|
||||
m_dnsServers.append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void AllowedDnsModel::removeDns(QModelIndex index)
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_dnsServers.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||
m_dnsServers.removeAt(index.row());
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
QStringList AllowedDnsModel::getCurrentDnsServers()
|
||||
{
|
||||
return m_dnsServers;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AllowedDnsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[IpRole] = "ip";
|
||||
return roles;
|
||||
}
|
||||
|
||||
void AllowedDnsModel::fillDnsServers()
|
||||
{
|
||||
m_dnsServers = m_settings->allowedDnsServers();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef ALLOWEDDNSMODEL_H
|
||||
#define ALLOWEDDNSMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "settings.h"
|
||||
|
||||
class AllowedDnsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
IpRole = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
explicit AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
bool addDns(const QString &ip);
|
||||
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
|
||||
void removeDns(QModelIndex index);
|
||||
QStringList getCurrentDnsServers();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
void fillDnsServers();
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QStringList m_dnsServers;
|
||||
};
|
||||
|
||||
#endif // ALLOWEDDNSMODEL_H
|
||||
@@ -48,15 +48,32 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
case ServiceDescriptionRole: {
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
return tr("Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to 200 "
|
||||
"Mb/s");
|
||||
return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online "
|
||||
"resources. "
|
||||
"Speeds up to 200 Mbps");
|
||||
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
|
||||
"more. YouTube is not included in the free plan.");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
case IsComponentVisibleRole: {
|
||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2;
|
||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|
||||
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium;
|
||||
}
|
||||
case HasExpiredWorkerRole: {
|
||||
for (int i = 0; i < m_issuedConfigsInfo.size(); i++) {
|
||||
QJsonObject issuedConfigObject = m_issuedConfigsInfo.at(i).toObject();
|
||||
|
||||
auto lastDownloaded = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::lastDownloaded).toString());
|
||||
auto workerLastUpdated = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString());
|
||||
|
||||
if (lastDownloaded < workerLastUpdated) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +97,8 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
||||
|
||||
m_accountInfoData = accountInfoData;
|
||||
|
||||
m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
@@ -108,12 +127,27 @@ QJsonArray ApiAccountInfoModel::getIssuedConfigsInfo()
|
||||
|
||||
QString ApiAccountInfoModel::getTelegramBotLink()
|
||||
{
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||
return tr("amnezia_free_support_bot");
|
||||
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
return tr("amnezia_premium_support_bot");
|
||||
}
|
||||
return "";
|
||||
return m_supportInfo.value(apiDefs::key::telegram).toString();
|
||||
}
|
||||
|
||||
QString ApiAccountInfoModel::getEmailLink()
|
||||
{
|
||||
return m_supportInfo.value(apiDefs::key::email).toString();
|
||||
}
|
||||
|
||||
QString ApiAccountInfoModel::getBillingEmailLink()
|
||||
{
|
||||
return m_supportInfo.value(apiDefs::key::billingEmail).toString();
|
||||
}
|
||||
|
||||
QString ApiAccountInfoModel::getSiteLink()
|
||||
{
|
||||
return m_supportInfo.value(apiDefs::key::websiteName).toString();
|
||||
}
|
||||
|
||||
QString ApiAccountInfoModel::getFullSiteLink()
|
||||
{
|
||||
return m_supportInfo.value(apiDefs::key::website).toString();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
||||
@@ -124,6 +158,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
||||
roles[ConnectedDevicesRole] = "connectedDevices";
|
||||
roles[ServiceDescriptionRole] = "serviceDescription";
|
||||
roles[IsComponentVisibleRole] = "isComponentVisible";
|
||||
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ public:
|
||||
ConnectedDevicesRole,
|
||||
ServiceDescriptionRole,
|
||||
EndDateRole,
|
||||
IsComponentVisibleRole
|
||||
IsComponentVisibleRole,
|
||||
HasExpiredWorkerRole
|
||||
};
|
||||
|
||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||
@@ -32,7 +33,12 @@ public slots:
|
||||
|
||||
QJsonArray getAvailableCountries();
|
||||
QJsonArray getIssuedConfigsInfo();
|
||||
|
||||
QString getTelegramBotLink();
|
||||
QString getEmailLink();
|
||||
QString getBillingEmailLink();
|
||||
QString getSiteLink();
|
||||
QString getFullSiteLink();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
@@ -50,6 +56,7 @@ private:
|
||||
AccountInfoData m_accountInfoData;
|
||||
QJsonArray m_availableCountries;
|
||||
QJsonArray m_issuedConfigsInfo;
|
||||
QJsonObject m_supportInfo;
|
||||
};
|
||||
|
||||
#endif // APIACCOUNTINFOMODEL_H
|
||||
|
||||
@@ -44,6 +44,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
||||
case IsIssuedRole: {
|
||||
return isIssued;
|
||||
}
|
||||
case IsWorkerExpiredRole: {
|
||||
return issuedConfigInfo.lastDownloaded < issuedConfigInfo.workerLastUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -114,5 +117,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
||||
roles[CountryCodeRole] = "countryCode";
|
||||
roles[CountryImageCodeRole] = "countryImageCode";
|
||||
roles[IsIssuedRole] = "isIssued";
|
||||
roles[IsWorkerExpiredRole] = "isWorkerExpired";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ public:
|
||||
CountryNameRole = Qt::UserRole + 1,
|
||||
CountryCodeRole,
|
||||
CountryImageCodeRole,
|
||||
IsIssuedRole
|
||||
IsIssuedRole,
|
||||
IsWorkerExpiredRole
|
||||
};
|
||||
|
||||
explicit ApiCountryModel(QObject *parent = nullptr);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "apiDevicesModel.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/api/apiDefs.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("ApiDevicesModel");
|
||||
|
||||
constexpr QLatin1String gatewayAccount("gateway_account");
|
||||
}
|
||||
|
||||
ApiDevicesModel::ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int ApiDevicesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_issuedConfigs.size();
|
||||
}
|
||||
|
||||
QVariant ApiDevicesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||
return QVariant();
|
||||
|
||||
IssuedConfigInfo issuedConfigInfo = m_issuedConfigs.at(index.row());
|
||||
|
||||
switch (role) {
|
||||
case OsVersionRole: {
|
||||
return issuedConfigInfo.osVersion;
|
||||
}
|
||||
case SupportTagRole: {
|
||||
return issuedConfigInfo.installationUuid;
|
||||
}
|
||||
case CountryCodeRole: {
|
||||
return issuedConfigInfo.countryCode;
|
||||
}
|
||||
case LastUpdateRole: {
|
||||
return QDateTime::fromString(issuedConfigInfo.lastDownloaded, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
|
||||
}
|
||||
case IsCurrentDeviceRole: {
|
||||
return issuedConfigInfo.installationUuid == m_settings->getInstallationUuid(false);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void ApiDevicesModel::updateModel(const QJsonArray &issuedConfigs)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_issuedConfigs.clear();
|
||||
for (int i = 0; i < issuedConfigs.size(); i++) {
|
||||
IssuedConfigInfo issuedConfigInfo;
|
||||
QJsonObject issuedConfigObject = issuedConfigs.at(i).toObject();
|
||||
|
||||
if (issuedConfigObject.value(apiDefs::key::sourceType).toString() != gatewayAccount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
issuedConfigInfo.installationUuid = issuedConfigObject.value(apiDefs::key::installationUuid).toString();
|
||||
issuedConfigInfo.workerLastUpdated = issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString();
|
||||
issuedConfigInfo.lastDownloaded = issuedConfigObject.value(apiDefs::key::lastDownloaded).toString();
|
||||
issuedConfigInfo.sourceType = issuedConfigObject.value(apiDefs::key::sourceType).toString();
|
||||
issuedConfigInfo.osVersion = issuedConfigObject.value(apiDefs::key::osVersion).toString();
|
||||
|
||||
issuedConfigInfo.countryName = issuedConfigObject.value(apiDefs::key::serverCountryName).toString();
|
||||
issuedConfigInfo.countryCode = issuedConfigObject.value(apiDefs::key::serverCountryCode).toString();
|
||||
|
||||
m_issuedConfigs.push_back(issuedConfigInfo);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ApiDevicesModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[OsVersionRole] = "osVersion";
|
||||
roles[SupportTagRole] = "supportTag";
|
||||
roles[CountryCodeRole] = "countryCode";
|
||||
roles[LastUpdateRole] = "lastUpdate";
|
||||
roles[IsCurrentDeviceRole] = "isCurrentDevice";
|
||||
return roles;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef APIDEVICESMODEL_H
|
||||
#define APIDEVICESMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonArray>
|
||||
#include <QVector>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
class ApiDevicesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
OsVersionRole = Qt::UserRole + 1,
|
||||
SupportTagRole,
|
||||
CountryCodeRole,
|
||||
LastUpdateRole,
|
||||
IsCurrentDeviceRole
|
||||
};
|
||||
|
||||
explicit ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
void updateModel(const QJsonArray &issuedConfigs);
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
struct IssuedConfigInfo
|
||||
{
|
||||
QString installationUuid;
|
||||
QString workerLastUpdated;
|
||||
QString lastDownloaded;
|
||||
QString sourceType;
|
||||
QString osVersion;
|
||||
|
||||
QString countryName;
|
||||
QString countryCode;
|
||||
};
|
||||
|
||||
QVector<IssuedConfigInfo> m_issuedConfigs;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
#endif // APIDEVICESMODEL_H
|
||||
@@ -65,11 +65,11 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
case CardDescriptionRole: {
|
||||
auto speed = apiServiceData.serviceInfo.speed;
|
||||
if (serviceType == serviceType::amneziaPremium) {
|
||||
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
||||
"Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.")
|
||||
return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. "
|
||||
"Access all websites and online resources. Speeds up to %1 Mbps.")
|
||||
.arg(speed);
|
||||
} else if (serviceType == serviceType::amneziaFree) {
|
||||
QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
||||
QString description = tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
|
||||
if (!isServiceAvailable) {
|
||||
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, "
|
||||
"return to the previous screen, and try again.</a>");
|
||||
@@ -79,10 +79,10 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
case ServiceDescriptionRole: {
|
||||
if (serviceType == serviceType::amneziaPremium) {
|
||||
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
||||
"Works for any sites with no restrictions.");
|
||||
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
|
||||
"Access all websites and online resources.");
|
||||
} else {
|
||||
return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
||||
return tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
|
||||
}
|
||||
}
|
||||
case IsServiceAvailableRole: {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "languageModel.h"
|
||||
|
||||
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: m_settings(settings), QAbstractListModel(parent)
|
||||
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
|
||||
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||
m_availableLanguages.push_back(
|
||||
LanguageModelData {getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
||||
m_availableLanguages.push_back(LanguageModelData { getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +48,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
|
||||
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return strLanguage;
|
||||
@@ -104,11 +101,12 @@ QString LanguageModel::getCurrentLanguageName()
|
||||
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
||||
}
|
||||
|
||||
QString LanguageModel::getCurrentSiteUrl()
|
||||
QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
||||
{
|
||||
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||
switch (language) {
|
||||
case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/amnezia/amnezia.org";
|
||||
default: return "https://amnezia.org";
|
||||
case LanguageSettings::AvailableLanguageEnum::Russian:
|
||||
return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
|
||||
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public slots:
|
||||
int getCurrentLanguageIndex();
|
||||
int getLineHeightAppend();
|
||||
QString getCurrentLanguageName();
|
||||
QString getCurrentSiteUrl();
|
||||
QString getCurrentSiteUrl(const QString &path = "");
|
||||
|
||||
signals:
|
||||
void updateTranslations(const QLocale &locale);
|
||||
|
||||
@@ -25,18 +25,61 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
|
||||
case Roles::PortRole: m_serverProtocolConfig.insert(config_key::port, value.toString()); break;
|
||||
|
||||
case Roles::ClientMtuRole: m_clientProtocolConfig.insert(config_key::mtu, value.toString()); break;
|
||||
case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
|
||||
case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
|
||||
case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
|
||||
case Roles::ClientJunkPacketCountRole:
|
||||
m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString());
|
||||
break;
|
||||
case Roles::ClientJunkPacketMinSizeRole:
|
||||
m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString());
|
||||
break;
|
||||
case Roles::ClientJunkPacketMaxSizeRole:
|
||||
m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialJunk1Role:
|
||||
m_clientProtocolConfig.insert(config_key::specialJunk1, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialJunk2Role:
|
||||
m_clientProtocolConfig.insert(config_key::specialJunk2, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialJunk3Role:
|
||||
m_clientProtocolConfig.insert(config_key::specialJunk3, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialJunk4Role:
|
||||
m_clientProtocolConfig.insert(config_key::specialJunk4, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialJunk5Role:
|
||||
m_clientProtocolConfig.insert(config_key::specialJunk5, value.toString());
|
||||
break;
|
||||
case Roles::ClientControlledJunk1Role:
|
||||
m_clientProtocolConfig.insert(config_key::controlledJunk1, value.toString());
|
||||
break;
|
||||
case Roles::ClientControlledJunk2Role:
|
||||
m_clientProtocolConfig.insert(config_key::controlledJunk2, value.toString());
|
||||
break;
|
||||
case Roles::ClientControlledJunk3Role:
|
||||
m_clientProtocolConfig.insert(config_key::controlledJunk3, value.toString());
|
||||
break;
|
||||
case Roles::ClientSpecialHandshakeTimeoutRole:
|
||||
m_clientProtocolConfig.insert(config_key::specialHandshakeTimeout, value.toString());
|
||||
break;
|
||||
|
||||
case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
|
||||
case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
|
||||
case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
|
||||
case Roles::ServerInitPacketJunkSizeRole: m_serverProtocolConfig.insert(config_key::initPacketJunkSize, value.toString()); break;
|
||||
case Roles::ServerJunkPacketCountRole:
|
||||
m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString());
|
||||
break;
|
||||
case Roles::ServerJunkPacketMinSizeRole:
|
||||
m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString());
|
||||
break;
|
||||
case Roles::ServerJunkPacketMaxSizeRole:
|
||||
m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString());
|
||||
break;
|
||||
case Roles::ServerInitPacketJunkSizeRole:
|
||||
m_serverProtocolConfig.insert(config_key::initPacketJunkSize, value.toString());
|
||||
break;
|
||||
case Roles::ServerResponsePacketJunkSizeRole:
|
||||
m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString());
|
||||
break;
|
||||
case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break;
|
||||
case Roles::ServerInitPacketMagicHeaderRole:
|
||||
m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString());
|
||||
break;
|
||||
case Roles::ServerResponsePacketMagicHeaderRole:
|
||||
m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString());
|
||||
break;
|
||||
@@ -66,16 +109,30 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const
|
||||
case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount);
|
||||
case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize);
|
||||
case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize);
|
||||
case Roles::ClientSpecialJunk1Role: return m_clientProtocolConfig.value(config_key::specialJunk1);
|
||||
case Roles::ClientSpecialJunk2Role: return m_clientProtocolConfig.value(config_key::specialJunk2);
|
||||
case Roles::ClientSpecialJunk3Role: return m_clientProtocolConfig.value(config_key::specialJunk3);
|
||||
case Roles::ClientSpecialJunk4Role: return m_clientProtocolConfig.value(config_key::specialJunk4);
|
||||
case Roles::ClientSpecialJunk5Role: return m_clientProtocolConfig.value(config_key::specialJunk5);
|
||||
case Roles::ClientControlledJunk1Role: return m_clientProtocolConfig.value(config_key::controlledJunk1);
|
||||
case Roles::ClientControlledJunk2Role: return m_clientProtocolConfig.value(config_key::controlledJunk2);
|
||||
case Roles::ClientControlledJunk3Role: return m_clientProtocolConfig.value(config_key::controlledJunk3);
|
||||
case Roles::ClientSpecialHandshakeTimeoutRole:
|
||||
return m_clientProtocolConfig.value(config_key::specialHandshakeTimeout);
|
||||
|
||||
case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount);
|
||||
case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize);
|
||||
case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize);
|
||||
case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize);
|
||||
case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize);
|
||||
case Roles::ServerResponsePacketJunkSizeRole:
|
||||
return m_serverProtocolConfig.value(config_key::responsePacketJunkSize);
|
||||
case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader);
|
||||
case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
case Roles::ServerTransportPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
case Roles::ServerResponsePacketMagicHeaderRole:
|
||||
return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
case Roles::ServerUnderloadPacketMagicHeaderRole:
|
||||
return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
case Roles::ServerTransportPacketMagicHeaderRole:
|
||||
return m_serverProtocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -90,12 +147,15 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
||||
|
||||
QJsonObject serverProtocolConfig = config.value(config_key::awg).toObject();
|
||||
|
||||
auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Awg), Proto::Awg);
|
||||
auto defaultTransportProto =
|
||||
ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Awg), Proto::Awg);
|
||||
m_serverProtocolConfig.insert(config_key::transport_proto,
|
||||
serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto));
|
||||
m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config);
|
||||
m_serverProtocolConfig[config_key::subnet_address] = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||
m_serverProtocolConfig[config_key::subnet_address] =
|
||||
serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
m_serverProtocolConfig[config_key::port] =
|
||||
serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||
m_serverProtocolConfig[config_key::junkPacketCount] =
|
||||
serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
m_serverProtocolConfig[config_key::junkPacketMinSize] =
|
||||
@@ -105,25 +165,52 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
||||
m_serverProtocolConfig[config_key::initPacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::responsePacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize)
|
||||
.toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::initPacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
m_serverProtocolConfig[config_key::responsePacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||
serverProtocolConfig.value(config_key::responsePacketMagicHeader)
|
||||
.toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||
m_serverProtocolConfig[config_key::underloadPacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||
serverProtocolConfig.value(config_key::underloadPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||
m_serverProtocolConfig[config_key::transportPacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||
serverProtocolConfig.value(config_key::transportPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||
|
||||
auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString();
|
||||
QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
|
||||
m_clientProtocolConfig[config_key::mtu] = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu);
|
||||
m_clientProtocolConfig[config_key::junkPacketCount] =
|
||||
clientProtocolConfig.value(config_key::junkPacketCount).toString(m_serverProtocolConfig[config_key::junkPacketCount].toString());
|
||||
clientProtocolConfig.value(config_key::junkPacketCount)
|
||||
.toString(m_serverProtocolConfig[config_key::junkPacketCount].toString());
|
||||
m_clientProtocolConfig[config_key::junkPacketMinSize] =
|
||||
clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString());
|
||||
clientProtocolConfig.value(config_key::junkPacketMinSize)
|
||||
.toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString());
|
||||
m_clientProtocolConfig[config_key::junkPacketMaxSize] =
|
||||
clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString());
|
||||
clientProtocolConfig.value(config_key::junkPacketMaxSize)
|
||||
.toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString());
|
||||
m_clientProtocolConfig[config_key::specialJunk1] =
|
||||
clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1);
|
||||
m_clientProtocolConfig[config_key::specialJunk2] =
|
||||
clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2);
|
||||
m_clientProtocolConfig[config_key::specialJunk3] =
|
||||
clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3);
|
||||
m_clientProtocolConfig[config_key::specialJunk4] =
|
||||
clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4);
|
||||
m_clientProtocolConfig[config_key::specialJunk5] =
|
||||
clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5);
|
||||
m_clientProtocolConfig[config_key::controlledJunk1] =
|
||||
clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1);
|
||||
m_clientProtocolConfig[config_key::controlledJunk2] =
|
||||
clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2);
|
||||
m_clientProtocolConfig[config_key::controlledJunk3] =
|
||||
clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3);
|
||||
m_clientProtocolConfig[config_key::specialHandshakeTimeout] =
|
||||
clientProtocolConfig.value(config_key::specialHandshakeTimeout)
|
||||
.toString(protocols::awg::defaultSpecialHandshakeTimeout);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
@@ -141,6 +228,15 @@ QJsonObject AwgConfigModel::getConfig()
|
||||
jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount];
|
||||
jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize];
|
||||
jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize];
|
||||
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1];
|
||||
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2];
|
||||
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3];
|
||||
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4];
|
||||
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5];
|
||||
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1];
|
||||
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2];
|
||||
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3];
|
||||
jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout];
|
||||
|
||||
m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
|
||||
}
|
||||
@@ -178,6 +274,15 @@ QHash<int, QByteArray> AwgConfigModel::roleNames() const
|
||||
roles[ClientJunkPacketCountRole] = "clientJunkPacketCount";
|
||||
roles[ClientJunkPacketMinSizeRole] = "clientJunkPacketMinSize";
|
||||
roles[ClientJunkPacketMaxSizeRole] = "clientJunkPacketMaxSize";
|
||||
roles[ClientSpecialJunk1Role] = "clientSpecialJunk1";
|
||||
roles[ClientSpecialJunk2Role] = "clientSpecialJunk2";
|
||||
roles[ClientSpecialJunk3Role] = "clientSpecialJunk3";
|
||||
roles[ClientSpecialJunk4Role] = "clientSpecialJunk4";
|
||||
roles[ClientSpecialJunk5Role] = "clientSpecialJunk5";
|
||||
roles[ClientControlledJunk1Role] = "clientControlledJunk1";
|
||||
roles[ClientControlledJunk2Role] = "clientControlledJunk2";
|
||||
roles[ClientControlledJunk3Role] = "clientControlledJunk3";
|
||||
roles[ClientSpecialHandshakeTimeoutRole] = "clientSpecialHandshakeTimeout";
|
||||
|
||||
roles[ServerJunkPacketCountRole] = "serverJunkPacketCount";
|
||||
roles[ServerJunkPacketMinSizeRole] = "serverJunkPacketMinSize";
|
||||
@@ -197,33 +302,61 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
|
||||
auto lastConfig = serverProtocolConfig.value(config_key::last_config).toString();
|
||||
QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
|
||||
clientMtu = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu);
|
||||
clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
|
||||
clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||
clientJunkPacketCount =
|
||||
clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
clientJunkPacketMinSize =
|
||||
clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
|
||||
clientJunkPacketMaxSize =
|
||||
clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||
clientSpecialJunk1 =
|
||||
clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1);
|
||||
clientSpecialJunk2 =
|
||||
clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2);
|
||||
clientSpecialJunk3 =
|
||||
clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3);
|
||||
clientSpecialJunk4 =
|
||||
clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4);
|
||||
clientSpecialJunk5 =
|
||||
clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5);
|
||||
clientControlledJunk1 =
|
||||
clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1);
|
||||
clientControlledJunk2 =
|
||||
clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2);
|
||||
clientControlledJunk3 =
|
||||
clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3);
|
||||
clientSpecialHandshakeTimeout = clientProtocolConfig.value(config_key::specialHandshakeTimeout)
|
||||
.toString(protocols::awg::defaultSpecialHandshakeTimeout);
|
||||
|
||||
subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
subnetAddress =
|
||||
serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||
serverJunkPacketCount = serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
serverJunkPacketMinSize = serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
|
||||
serverJunkPacketMaxSize = serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||
serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
serverResponsePacketJunkSize =
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
serverInitPacketMagicHeader =
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
serverResponsePacketMagicHeader =
|
||||
serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||
serverUnderloadPacketMagicHeader =
|
||||
serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||
serverTransportPacketMagicHeader =
|
||||
serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||
serverJunkPacketCount =
|
||||
serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
serverJunkPacketMinSize =
|
||||
serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
|
||||
serverJunkPacketMaxSize =
|
||||
serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||
serverInitPacketJunkSize =
|
||||
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
serverResponsePacketJunkSize = serverProtocolConfig.value(config_key::responsePacketJunkSize)
|
||||
.toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
serverInitPacketMagicHeader = serverProtocolConfig.value(config_key::initPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
serverResponsePacketMagicHeader = serverProtocolConfig.value(config_key::responsePacketMagicHeader)
|
||||
.toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||
serverUnderloadPacketMagicHeader = serverProtocolConfig.value(config_key::underloadPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||
serverTransportPacketMagicHeader = serverProtocolConfig.value(config_key::transportPacketMagicHeader)
|
||||
.toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||
}
|
||||
|
||||
bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
|
||||
{
|
||||
if (subnetAddress != other.subnetAddress || port != other.port || serverJunkPacketCount != other.serverJunkPacketCount
|
||||
|| serverJunkPacketMinSize != other.serverJunkPacketMinSize || serverJunkPacketMaxSize != other.serverJunkPacketMaxSize
|
||||
|| serverInitPacketJunkSize != other.serverInitPacketJunkSize || serverResponsePacketJunkSize != other.serverResponsePacketJunkSize
|
||||
|| serverJunkPacketMinSize != other.serverJunkPacketMinSize
|
||||
|| serverJunkPacketMaxSize != other.serverJunkPacketMaxSize
|
||||
|| serverInitPacketJunkSize != other.serverInitPacketJunkSize
|
||||
|| serverResponsePacketJunkSize != other.serverResponsePacketJunkSize
|
||||
|| serverInitPacketMagicHeader != other.serverInitPacketMagicHeader
|
||||
|| serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader
|
||||
|| serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader
|
||||
@@ -236,7 +369,13 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
|
||||
bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const
|
||||
{
|
||||
if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount
|
||||
|| clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize) {
|
||||
|| clientJunkPacketMinSize != other.clientJunkPacketMinSize
|
||||
|| clientJunkPacketMaxSize != other.clientJunkPacketMaxSize || clientSpecialJunk1 != other.clientSpecialJunk1
|
||||
|| clientSpecialJunk2 != other.clientSpecialJunk2 || clientSpecialJunk3 != other.clientSpecialJunk3
|
||||
|| clientSpecialJunk4 != other.clientSpecialJunk4 || clientSpecialJunk5 != other.clientSpecialJunk5
|
||||
|| clientControlledJunk1 != other.clientControlledJunk1 || clientControlledJunk2 != other.clientControlledJunk2
|
||||
|| clientControlledJunk3 != other.clientControlledJunk3
|
||||
|| clientSpecialHandshakeTimeout != other.clientSpecialHandshakeTimeout) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -22,6 +22,15 @@ struct AwgConfig
|
||||
QString clientJunkPacketCount;
|
||||
QString clientJunkPacketMinSize;
|
||||
QString clientJunkPacketMaxSize;
|
||||
QString clientSpecialJunk1;
|
||||
QString clientSpecialJunk2;
|
||||
QString clientSpecialJunk3;
|
||||
QString clientSpecialJunk4;
|
||||
QString clientSpecialJunk5;
|
||||
QString clientControlledJunk1;
|
||||
QString clientControlledJunk2;
|
||||
QString clientControlledJunk3;
|
||||
QString clientSpecialHandshakeTimeout;
|
||||
|
||||
QString serverJunkPacketCount;
|
||||
QString serverJunkPacketMinSize;
|
||||
@@ -35,7 +44,6 @@ struct AwgConfig
|
||||
|
||||
bool hasEqualServerSettings(const AwgConfig &other) const;
|
||||
bool hasEqualClientSettings(const AwgConfig &other) const;
|
||||
|
||||
};
|
||||
|
||||
class AwgConfigModel : public QAbstractListModel
|
||||
@@ -51,6 +59,15 @@ public:
|
||||
ClientJunkPacketCountRole,
|
||||
ClientJunkPacketMinSizeRole,
|
||||
ClientJunkPacketMaxSizeRole,
|
||||
ClientSpecialJunk1Role,
|
||||
ClientSpecialJunk2Role,
|
||||
ClientSpecialJunk3Role,
|
||||
ClientSpecialJunk4Role,
|
||||
ClientSpecialJunk5Role,
|
||||
ClientControlledJunk1Role,
|
||||
ClientControlledJunk2Role,
|
||||
ClientControlledJunk3Role,
|
||||
ClientSpecialHandshakeTimeoutRole,
|
||||
|
||||
ServerJunkPacketCountRole,
|
||||
ServerJunkPacketMinSizeRole,
|
||||
@@ -60,7 +77,7 @@ public:
|
||||
ServerInitPacketMagicHeaderRole,
|
||||
ServerResponsePacketMagicHeaderRole,
|
||||
ServerUnderloadPacketMagicHeaderRole,
|
||||
ServerTransportPacketMagicHeaderRole
|
||||
ServerTransportPacketMagicHeaderRole,
|
||||
};
|
||||
|
||||
explicit AwgConfigModel(QObject *parent = nullptr);
|
||||
|
||||
@@ -20,6 +20,7 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i
|
||||
|
||||
switch (role) {
|
||||
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
|
||||
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QList { role });
|
||||
@@ -34,6 +35,7 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
switch (role) {
|
||||
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite);
|
||||
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -67,6 +69,7 @@ QHash<int, QByteArray> XrayConfigModel::roleNames() const
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[SiteRole] = "site";
|
||||
roles[PortRole] = "port";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ class XrayConfigModel : public QAbstractListModel
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
SiteRole
|
||||
SiteRole,
|
||||
PortRole
|
||||
};
|
||||
|
||||
explicit XrayConfigModel(QObject *parent = nullptr);
|
||||
|
||||
@@ -348,6 +348,25 @@ void ServersModel::removeServer()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void ServersModel::removeServer(const int serverIndex)
|
||||
{
|
||||
beginResetModel();
|
||||
m_settings->removeServer(serverIndex);
|
||||
m_servers = m_settings->serversArray();
|
||||
|
||||
if (m_settings->defaultServerIndex() == serverIndex) {
|
||||
setDefaultServerIndex(0);
|
||||
} else if (m_settings->defaultServerIndex() > serverIndex) {
|
||||
setDefaultServerIndex(m_settings->defaultServerIndex() - 1);
|
||||
}
|
||||
|
||||
if (m_settings->serversCount() == 0) {
|
||||
setDefaultServerIndex(-1);
|
||||
}
|
||||
setProcessedServerIndex(m_defaultServerIndex);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ServersModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
@@ -90,6 +90,7 @@ public slots:
|
||||
void addServer(const QJsonObject &server);
|
||||
void editServer(const QJsonObject &server, const int serverIndex);
|
||||
void removeServer();
|
||||
void removeServer(const int serverIndex);
|
||||
|
||||
QJsonObject getServerConfig(const int serverIndex);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/premium")
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ Rectangle {
|
||||
Layout.rightMargin: 10
|
||||
Layout.leftMargin: 10
|
||||
|
||||
text: qsTr("Amnezia Premium - for access to any website")
|
||||
text: qsTr("Amnezia Premium - for access to all websites and online resources")
|
||||
color: AmneziaStyle.color.pearlGray
|
||||
|
||||
lineHeight: 18
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user