Compare commits
83 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1909d3c94e | ||
![]() |
10a107716c | ||
![]() |
5445e6637b | ||
![]() |
2380cd5cfb | ||
![]() |
42661618dc | ||
![]() |
8a7e901d7a | ||
![]() |
f8bea71716 | ||
![]() |
efcc0b7efc | ||
![]() |
4d17e913b5 | ||
![]() |
b341934863 | ||
![]() |
127f8ed3bb | ||
![]() |
9dca80de18 | ||
![]() |
b0a6bcc055 | ||
![]() |
f0626e2eca | ||
![]() |
979ab42c5a | ||
![]() |
e152e84ddc | ||
![]() |
2605978889 | ||
![]() |
a2d30efaab | ||
![]() |
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 |
186 changed files with 12355 additions and 5272 deletions
88
.github/workflows/deploy.yml
vendored
88
.github/workflows/deploy.yml
vendored
|
@ -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,18 +249,82 @@ jobs:
|
|||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-MacOS:
|
||||
Build-MacOS-old:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||
QT_VERSION: 6.4.3
|
||||
QIF_VERSION: 4.6
|
||||
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'
|
||||
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: '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"
|
||||
bash deploy/build_macos.sh
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_old_installer
|
||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||
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
|
||||
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'
|
||||
|
@ -275,11 +345,6 @@ jobs:
|
|||
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
|
||||
|
@ -293,14 +358,13 @@ jobs:
|
|||
- 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_installer
|
||||
path: AmneziaVPN.dmg
|
||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
|
@ -324,6 +388,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'
|
||||
|
|
2
.github/workflows/tag-deploy.yml
vendored
2
.github/workflows/tag-deploy.yml
vendored
|
@ -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'
|
||||
|
|
61
.github/workflows/tag-upload.yml
vendored
61
.github/workflows/tag-upload.yml
vendored
|
@ -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 }}
|
||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -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
|
||||
|
|
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -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,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.4.3
|
||||
project(${PROJECT} VERSION 4.8.8.1
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2080)
|
||||
set(APP_ANDROID_VERSION_CODE 2087)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
|
20
README_RU.md
20
README_RU.md
|
@ -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 @@
|
|||
Subproject commit e555c78bcf44070d5c88bcca54480732c9164f18
|
||||
Subproject commit 840b7b070e6ac8b90dda2fac6e98859b23727c0c
|
2
client/3rd/amneziawg-apple
vendored
2
client/3rd/amneziawg-apple
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c
|
||||
Subproject commit 811af0a83b3faeade89a9093a588595666d32066
|
|
@ -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()
|
||||
|
|
|
@ -120,10 +120,21 @@ open class Wireguard : Protocol() {
|
|||
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
||||
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
||||
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
||||
configData.optStringOrNull("S3")?.let { setS3(it.toInt()) }
|
||||
configData.optStringOrNull("S4")?.let { setS4(it.toInt()) }
|
||||
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
||||
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) }
|
||||
configData.optStringOrNull("I2")?.let { setI2(it) }
|
||||
configData.optStringOrNull("I3")?.let { setI3(it) }
|
||||
configData.optStringOrNull("I4")?.let { setI4(it) }
|
||||
configData.optStringOrNull("I5")?.let { setI5(it) }
|
||||
configData.optStringOrNull("J1")?.let { setJ1(it) }
|
||||
configData.optStringOrNull("J2")?.let { setJ2(it) }
|
||||
configData.optStringOrNull("J3")?.let { setJ3(it) }
|
||||
configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) }
|
||||
}
|
||||
|
||||
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
|
|
|
@ -20,10 +20,21 @@ open class WireguardConfig protected constructor(
|
|||
val jmax: Int?,
|
||||
val s1: Int?,
|
||||
val s2: Int?,
|
||||
val s3: Int?,
|
||||
val s4: Int?,
|
||||
val h1: Long?,
|
||||
val h2: Long?,
|
||||
val h3: Long?,
|
||||
val h4: Long?
|
||||
val h4: Long?,
|
||||
var i1: String?,
|
||||
var i2: String?,
|
||||
var i3: String?,
|
||||
var i4: String?,
|
||||
var i5: String?,
|
||||
var j1: String?,
|
||||
var j2: String?,
|
||||
var j3: String?,
|
||||
var itime: Int?
|
||||
) : ProtocolConfig(protocolConfigBuilder) {
|
||||
|
||||
protected constructor(builder: Builder) : this(
|
||||
|
@ -39,10 +50,21 @@ open class WireguardConfig protected constructor(
|
|||
builder.jmax,
|
||||
builder.s1,
|
||||
builder.s2,
|
||||
builder.s3,
|
||||
builder.s4,
|
||||
builder.h1,
|
||||
builder.h2,
|
||||
builder.h3,
|
||||
builder.h4
|
||||
builder.h4,
|
||||
builder.i1,
|
||||
builder.i2,
|
||||
builder.i3,
|
||||
builder.i4,
|
||||
builder.i5,
|
||||
builder.j1,
|
||||
builder.j2,
|
||||
builder.j3,
|
||||
builder.itime
|
||||
)
|
||||
|
||||
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
||||
|
@ -61,10 +83,21 @@ open class WireguardConfig protected constructor(
|
|||
appendLine("jmax=$jmax")
|
||||
appendLine("s1=$s1")
|
||||
appendLine("s2=$s2")
|
||||
s3?.let { appendLine("s3=$it") }
|
||||
s4?.let { appendLine("s4=$it") }
|
||||
appendLine("h1=$h1")
|
||||
appendLine("h2=$h2")
|
||||
appendLine("h3=$h3")
|
||||
appendLine("h4=$h4")
|
||||
i1?.let { appendLine("i1=$it") }
|
||||
i2?.let { appendLine("i2=$it") }
|
||||
i3?.let { appendLine("i3=$it") }
|
||||
i4?.let { appendLine("i4=$it") }
|
||||
i5?.let { appendLine("i5=$it") }
|
||||
j1?.let { appendLine("j1=$it") }
|
||||
j2?.let { appendLine("j2=$it") }
|
||||
j3?.let { appendLine("j3=$it") }
|
||||
itime?.let { appendLine("itime=$it") }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,10 +150,21 @@ open class WireguardConfig protected constructor(
|
|||
internal var jmax: Int? = null
|
||||
internal var s1: Int? = null
|
||||
internal var s2: Int? = null
|
||||
internal var s3: Int? = null
|
||||
internal var s4: Int? = null
|
||||
internal var h1: Long? = null
|
||||
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 }
|
||||
|
||||
|
@ -139,10 +183,21 @@ open class WireguardConfig protected constructor(
|
|||
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
||||
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
||||
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
||||
fun setS3(s3: Int) = apply { this.s3 = s3 }
|
||||
fun setS4(s4: Int) = apply { this.s4 = s4 }
|
||||
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
||||
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) }
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "awg_configurator.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
@ -39,6 +40,20 @@ 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::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
|
||||
// jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
|
||||
|
||||
// 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)
|
||||
|
@ -117,22 +118,22 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
|
||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
||||
config.replace(dnsRegex, "");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
@ -166,10 +167,15 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
|
|||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
|
||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
||||
config.replace(dnsRegex, "");
|
||||
}
|
||||
|
||||
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,21 @@ 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 supportedProtocols("supported_protocols");
|
||||
|
||||
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 +53,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)
|
||||
|
@ -47,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());
|
||||
|
||||
|
@ -129,6 +133,9 @@ 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());
|
||||
|
||||
|
@ -141,6 +148,9 @@ void CoreController::initControllers()
|
|||
|
||||
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()
|
||||
|
@ -213,6 +223,9 @@ void CoreController::initSignalHandlers()
|
|||
initAutoConnectHandler();
|
||||
initAmneziaDnsToggledHandler();
|
||||
initPrepareConfigHandler();
|
||||
initImportPremiumV2VpnKeyHandler();
|
||||
initShowMigrationDrawerHandler();
|
||||
initStrictKillSwitchHandler();
|
||||
}
|
||||
|
||||
void CoreController::initNotificationHandler()
|
||||
|
@ -238,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);
|
||||
|
@ -339,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"
|
||||
|
@ -80,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;
|
||||
|
@ -102,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;
|
||||
|
@ -112,6 +120,7 @@ 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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -247,6 +286,9 @@ QStringList GatewayController::getProxyUrls()
|
|||
}
|
||||
return endpoints;
|
||||
} else {
|
||||
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
||||
qDebug() << "go to the next storage endpoint";
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
@ -257,17 +299,36 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
|
|||
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::NoError && checkEncryption) {
|
||||
} 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));
|
||||
|
||||
|
|
|
@ -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 &) {
|
||||
|
@ -349,7 +349,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
||||
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
||||
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
||||
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
|
||||
|
@ -366,8 +366,13 @@ 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::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
|
||||
// != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
|
||||
// || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
|
||||
// != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize))
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -375,7 +380,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -383,6 +388,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 +451,24 @@ 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;
|
||||
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
|
||||
return ErrorCode::DockerPullRateLimit;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
|
@ -625,6 +646,9 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||
|
||||
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
|
||||
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
|
||||
|
||||
// Socks5 proxy vars
|
||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
||||
|
@ -709,7 +733,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 +781,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 +794,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;
|
||||
}
|
||||
|
@ -807,7 +835,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
|||
|
||||
if (stdOut.contains("Packet manager not found"))
|
||||
return ErrorCode::ServerPacketManagerError;
|
||||
if (stdOut.contains("fuser not installed"))
|
||||
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
||||
return ErrorCode::NoError;
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
|
|
|
@ -54,6 +54,13 @@ namespace amnezia
|
|||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
ServerSudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserDirectoryNotAccessible = 208,
|
||||
ServerUserNotAllowedInSudoers = 209,
|
||||
ServerUserPasswordRequired = 210,
|
||||
ServerDockerOnCgroupsV2 = 211,
|
||||
ServerCgroupMountpoint = 212,
|
||||
DockerPullRateLimit = 213,
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequestDeniedError = 300,
|
||||
|
@ -111,6 +118,8 @@ namespace amnezia
|
|||
ApiServicesMissingError = 1107,
|
||||
ApiConfigLimitError = 1108,
|
||||
ApiNotFoundError = 1109,
|
||||
ApiMigrationError = 1110,
|
||||
ApiUpdateRequestError = 1111,
|
||||
|
||||
// QFile errors
|
||||
OpenError = 1200,
|
||||
|
|
|
@ -20,8 +20,15 @@ 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;
|
||||
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
|
@ -68,6 +75,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;
|
||||
}
|
||||
}
|
||||
|
@ -170,11 +169,14 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
|||
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
||||
QList<QHostAddress> resolvers;
|
||||
resolvers.append(QHostAddress(config.m_dnsServer));
|
||||
resolvers.append(QHostAddress(config.m_primaryDnsServer));
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
resolvers.append(QHostAddress(config.m_secondaryDnsServer));
|
||||
}
|
||||
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||
}
|
||||
|
||||
|
@ -280,15 +282,26 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
||||
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
||||
|
||||
if (!obj.contains("dnsServer")) {
|
||||
config.m_dnsServer = QString();
|
||||
if (!obj.contains("primaryDnsServer")) {
|
||||
config.m_primaryDnsServer = QString();
|
||||
} else {
|
||||
QJsonValue value = obj.value("dnsServer");
|
||||
QJsonValue value = obj.value("primaryDnsServer");
|
||||
if (!value.isString()) {
|
||||
logger.error() << "dnsServer is not a string";
|
||||
return false;
|
||||
}
|
||||
config.m_dnsServer = value.toString();
|
||||
config.m_primaryDnsServer = value.toString();
|
||||
}
|
||||
|
||||
if (!obj.contains("secondaryDnsServer")) {
|
||||
config.m_secondaryDnsServer = QString();
|
||||
} else {
|
||||
QJsonValue value = obj.value("secondaryDnsServer");
|
||||
if (!value.isString()) {
|
||||
logger.error() << "dnsServer is not a string";
|
||||
return false;
|
||||
}
|
||||
config.m_secondaryDnsServer = value.toString();
|
||||
}
|
||||
|
||||
if (!obj.contains("hopType")) {
|
||||
|
@ -371,6 +384,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();
|
||||
|
||||
|
@ -389,6 +405,13 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||
if (!obj.value("S2").isNull()) {
|
||||
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
||||
}
|
||||
if (!obj.value("S3").isNull()) {
|
||||
config.m_cookieReplyPacketJunkSize = obj.value("S3").toString();
|
||||
}
|
||||
if (!obj.value("S4").isNull()) {
|
||||
config.m_transportPacketJunkSize = obj.value("S4").toString();
|
||||
}
|
||||
|
||||
if (!obj.value("H1").isNull()) {
|
||||
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
||||
}
|
||||
|
@ -402,6 +425,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;
|
||||
}
|
||||
|
||||
|
@ -444,7 +495,7 @@ bool Daemon::deactivate(bool emitSignals) {
|
|||
|
||||
m_connections.clear();
|
||||
// Delete the interface
|
||||
return wgutils()->deleteInterface();
|
||||
return wgutils()->deleteInterface();
|
||||
}
|
||||
|
||||
QString Daemon::logs() {
|
||||
|
|
|
@ -28,7 +28,8 @@ QJsonObject InterfaceConfig::toJson() const {
|
|||
(m_hopType == InterfaceConfig::SingleHop)) {
|
||||
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
||||
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
||||
json.insert("dnsServer", QJsonValue(m_dnsServer));
|
||||
json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer));
|
||||
json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer));
|
||||
}
|
||||
|
||||
QJsonArray allowedIPAddesses;
|
||||
|
@ -48,6 +49,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));
|
||||
|
@ -93,11 +101,15 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||
out << "MTU = " << m_deviceMTU << "\n";
|
||||
}
|
||||
|
||||
if (!m_dnsServer.isNull()) {
|
||||
QStringList dnsServers(m_dnsServer);
|
||||
if (!m_primaryDnsServer.isNull()) {
|
||||
QStringList dnsServers;
|
||||
dnsServers.append(m_primaryDnsServer);
|
||||
if (!m_secondaryDnsServer.isNull()) {
|
||||
dnsServers.append(m_secondaryDnsServer);
|
||||
}
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (m_dnsServer == m_serverIpv4Gateway) {
|
||||
if (m_primaryDnsServer == m_serverIpv4Gateway) {
|
||||
dnsServers.append(m_serverIpv6Gateway);
|
||||
}
|
||||
out << "DNS = " << dnsServers.join(", ") << "\n";
|
||||
|
@ -118,6 +130,12 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||
if (!m_responsePacketJunkSize.isNull()) {
|
||||
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!m_cookieReplyPacketJunkSize.isNull()) {
|
||||
out << "S3 = " << m_cookieReplyPacketJunkSize << "\n";
|
||||
}
|
||||
if (!m_transportPacketJunkSize.isNull()) {
|
||||
out << "S4 = " << m_transportPacketJunkSize << "\n";
|
||||
}
|
||||
if (!m_initPacketMagicHeader.isNull()) {
|
||||
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
|
@ -131,6 +149,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"
|
||||
|
@ -31,12 +32,14 @@ class InterfaceConfig {
|
|||
QString m_serverIpv4AddrIn;
|
||||
QString m_serverPskKey;
|
||||
QString m_serverIpv6AddrIn;
|
||||
QString m_dnsServer;
|
||||
QString m_primaryDnsServer;
|
||||
QString m_secondaryDnsServer;
|
||||
int m_serverPort = 0;
|
||||
int m_deviceMTU = 1420;
|
||||
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;
|
||||
|
@ -47,10 +50,15 @@ class InterfaceConfig {
|
|||
QString m_junkPacketMaxSize;
|
||||
QString m_initPacketJunkSize;
|
||||
QString m_responsePacketJunkSize;
|
||||
QString m_cookieReplyPacketJunkSize;
|
||||
QString m_transportPacketJunkSize;
|
||||
QString m_initPacketMagicHeader;
|
||||
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(
|
||||
|
|
|
@ -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
|
||||
|
@ -148,7 +149,14 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
|
||||
json.insert("primaryDnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
|
||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||
if (!rawConfig.value(amnezia::config_key::dns1).toString().
|
||||
contains(amnezia::protocols::dns::amneziaDnsIp)) {
|
||||
json.insert("secondaryDnsServer", rawConfig.value(amnezia::config_key::dns2));
|
||||
}
|
||||
|
||||
QJsonArray jsAllowedIPAddesses;
|
||||
|
||||
|
@ -226,6 +234,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) {
|
||||
|
@ -234,28 +244,61 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||
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()
|
||||
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
|
||||
&& !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
|
||||
&& !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));
|
||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ struct WGConfig: Decodable {
|
|||
let initPacketMagicHeader, responsePacketMagicHeader: String?
|
||||
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
||||
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
||||
let initPacketJunkSize, responsePacketJunkSize: String?
|
||||
let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: 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
|
||||
|
@ -23,7 +26,10 @@ struct WGConfig: Decodable {
|
|||
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
|
||||
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
||||
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2", cookieReplyPacketJunkSize = "S3", transportPacketJunkSize = "S4"
|
||||
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
|
||||
|
@ -40,19 +46,59 @@ struct WGConfig: Decodable {
|
|||
}
|
||||
|
||||
var settings: String {
|
||||
junkPacketCount == nil ? "" :
|
||||
"""
|
||||
Jc = \(junkPacketCount!)
|
||||
Jmin = \(junkPacketMinSize!)
|
||||
Jmax = \(junkPacketMaxSize!)
|
||||
S1 = \(initPacketJunkSize!)
|
||||
S2 = \(responsePacketJunkSize!)
|
||||
H1 = \(initPacketMagicHeader!)
|
||||
H2 = \(responsePacketMagicHeader!)
|
||||
H3 = \(underloadPacketMagicHeader!)
|
||||
H4 = \(transportPacketMagicHeader!)
|
||||
guard junkPacketCount != nil else { return "" }
|
||||
|
||||
var settingsLines: [String] = []
|
||||
|
||||
// Required parameters when junkPacketCount is present
|
||||
settingsLines.append("Jc = \(junkPacketCount!)")
|
||||
settingsLines.append("Jmin = \(junkPacketMinSize!)")
|
||||
settingsLines.append("Jmax = \(junkPacketMaxSize!)")
|
||||
settingsLines.append("S1 = \(initPacketJunkSize!)")
|
||||
settingsLines.append("S2 = \(responsePacketJunkSize!)")
|
||||
|
||||
settingsLines.append("H1 = \(initPacketMagicHeader!)")
|
||||
settingsLines.append("H2 = \(responsePacketMagicHeader!)")
|
||||
settingsLines.append("H3 = \(underloadPacketMagicHeader!)")
|
||||
settingsLines.append("H4 = \(transportPacketMagicHeader!)")
|
||||
|
||||
"""
|
||||
// Optional parameters - only add if not nil and not empty
|
||||
if let s3 = cookieReplyPacketJunkSize, !s3.isEmpty {
|
||||
settingsLines.append("S3 = \(s3)")
|
||||
}
|
||||
if let s4 = transportPacketJunkSize, !s4.isEmpty {
|
||||
settingsLines.append("S4 = \(s4)")
|
||||
}
|
||||
|
||||
if let i1 = specialJunk1, !i1.isEmpty {
|
||||
settingsLines.append("I1 = \(i1)")
|
||||
}
|
||||
if let i2 = specialJunk2, !i2.isEmpty {
|
||||
settingsLines.append("I2 = \(i2)")
|
||||
}
|
||||
if let i3 = specialJunk3, !i3.isEmpty {
|
||||
settingsLines.append("I3 = \(i3)")
|
||||
}
|
||||
if let i4 = specialJunk4, !i4.isEmpty {
|
||||
settingsLines.append("I4 = \(i4)")
|
||||
}
|
||||
if let i5 = specialJunk5, !i5.isEmpty {
|
||||
settingsLines.append("I5 = \(i5)")
|
||||
}
|
||||
if let j1 = controlledJunk1, !j1.isEmpty {
|
||||
settingsLines.append("J1 = \(j1)")
|
||||
}
|
||||
if let j2 = controlledJunk2, !j2.isEmpty {
|
||||
settingsLines.append("J2 = \(j2)")
|
||||
}
|
||||
if let j3 = controlledJunk3, !j3.isEmpty {
|
||||
settingsLines.append("J3 = \(j3)")
|
||||
}
|
||||
if let itime = specialHandshakeTimeout, !itime.isEmpty {
|
||||
settingsLines.append("Itime = \(itime)")
|
||||
}
|
||||
|
||||
return settingsLines.joined(separator: "\n")
|
||||
}
|
||||
|
||||
var str: String {
|
||||
|
|
|
@ -507,6 +507,8 @@ bool IosController::setupWireGuard()
|
|||
|
||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||
|
||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||
|
@ -605,11 +607,23 @@ bool IosController::setupAwg()
|
|||
|
||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||
|
||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
||||
|
||||
wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]);
|
||||
wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]);
|
||||
wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]);
|
||||
wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]);
|
||||
wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]);
|
||||
wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]);
|
||||
wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]);
|
||||
wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]);
|
||||
wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]);
|
||||
|
||||
QJsonDocument wgConfigDoc(wgConfig);
|
||||
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||
|
||||
|
@ -794,9 +808,9 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||
if (!qtController) return;
|
||||
|
||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||
|
||||
|
||||
__block bool isAccepted = false;
|
||||
|
||||
|
||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||
isAccepted = completed;
|
||||
emit finished();
|
||||
|
@ -808,11 +822,11 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||
popController.sourceView = qtController.view;
|
||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||
}
|
||||
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
|
||||
return isAccepted;
|
||||
}
|
||||
|
||||
|
@ -826,7 +840,7 @@ QString IosController::openFile() {
|
|||
if (!qtController) return;
|
||||
|
||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||
|
||||
|
||||
__block QString filePath;
|
||||
|
||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||
|
@ -841,7 +855,7 @@ QString IosController::openFile() {
|
|||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
@ -119,6 +121,12 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
|||
if (!config.m_responsePacketJunkSize.isEmpty()) {
|
||||
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
|
||||
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketJunkSize.isEmpty()) {
|
||||
out << "s4=" << config.m_transportPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketMagicHeader.isEmpty()) {
|
||||
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
|
@ -132,13 +140,26 @@ 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);
|
||||
} else {
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
params.dnsServers.append(config.m_primaryDnsServer);
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
params.dnsServers.append(config.m_secondaryDnsServer);
|
||||
}
|
||||
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
|
@ -182,7 +203,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;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,16 @@ namespace {
|
|||
|
||||
#include "macosfirewall.h"
|
||||
|
||||
#define ResourceDir qApp->applicationDirPath() + "/pf"
|
||||
#define DaemonDataDir qApp->applicationDirPath() + "/pf"
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
// Read-only rules bundled with the application.
|
||||
#define ResourceDir (qApp->applicationDirPath() + "/pf")
|
||||
|
||||
// Writable location that does NOT live inside the signed bundle. Using a
|
||||
// constant path under /Library/Application Support keeps the signature intact
|
||||
// and is accessible to the root helper.
|
||||
#define DaemonDataDir QStringLiteral("/Library/Application Support/AmneziaVPN/pf")
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
|
@ -121,6 +129,8 @@ void MacOSFirewall::install()
|
|||
logger.info() << "Installing PF root anchor";
|
||||
|
||||
installRootAnchors();
|
||||
// Ensure writable directory exists, then store the token there.
|
||||
QDir().mkpath(DaemonDataDir);
|
||||
execute(QStringLiteral("pfctl -E 2>&1 | grep -F 'Token : ' | cut -c9- > '%1/pf.token'").arg(DaemonDataDir));
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
@ -117,6 +119,12 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||
if (!config.m_responsePacketJunkSize.isEmpty()) {
|
||||
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
|
||||
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketJunkSize.isEmpty()) {
|
||||
out << "s4=" << config.m_transportPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketMagicHeader.isEmpty()) {
|
||||
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
|
@ -130,30 +138,43 @@ bool WireguardUtilsMacos::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);
|
||||
} else {
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_primaryDnsServer);
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
params.dnsServers.append(config.m_secondaryDnsServer);
|
||||
}
|
||||
|
||||
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
|
||||
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
}
|
||||
return (err == 0);
|
||||
}
|
||||
|
@ -180,7 +201,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);
|
||||
|
@ -245,15 +291,15 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
|||
"Block Internet", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
if (!config.m_dnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_dnsServer), 53, HIGH_WEIGHT,
|
||||
if (!config.m_primaryDnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
// In some cases, we might configure a 2nd DNS server for IPv6, however
|
||||
// this should probably be cleaned up by converting m_dnsServer into
|
||||
// a QStringList instead.
|
||||
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
|
||||
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
|
||||
config.m_serverPublicKey)) {
|
||||
|
@ -262,12 +308,37 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_secondaryDnsServer), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
// In some cases, we might configure a 2nd DNS server for IPv6, however
|
||||
// this should probably be cleaned up by converting m_dnsServer into
|
||||
// a QStringList instead.
|
||||
if (config.m_secondaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
|
||||
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
|
||||
config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 +384,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")
|
||||
|
@ -132,6 +130,7 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
|||
// Enable the windows firewall
|
||||
NET_IFINDEX ifindex;
|
||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||
m_firewall->allowAllTraffic();
|
||||
m_firewall->enableInterface(ifindex);
|
||||
}
|
||||
|
||||
|
@ -269,6 +268,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;
|
||||
|
@ -338,7 +343,7 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
|||
// killSwitch toggle
|
||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index());
|
||||
}
|
||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||
|
|
|
@ -72,10 +72,21 @@ namespace amnezia
|
|||
constexpr char junkPacketMaxSize[] = "Jmax";
|
||||
constexpr char initPacketJunkSize[] = "S1";
|
||||
constexpr char responsePacketJunkSize[] = "S2";
|
||||
constexpr char cookieReplyPacketJunkSize[] = "S3";
|
||||
constexpr char transportPacketJunkSize[] = "S4";
|
||||
constexpr char initPacketMagicHeader[] = "H1";
|
||||
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,12 +106,16 @@ namespace amnezia
|
|||
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
||||
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
||||
|
||||
constexpr char allowedDnsServers[] = "allowedDnsServers";
|
||||
|
||||
constexpr char killSwitchOption[] = "killSwitchOption";
|
||||
|
||||
constexpr char crc[] = "crc";
|
||||
|
||||
constexpr char clientId[] = "clientId";
|
||||
|
||||
constexpr char nameOverriddenByUser[] = "nameOverriddenByUser";
|
||||
|
||||
}
|
||||
|
||||
namespace protocols
|
||||
|
@ -212,10 +227,22 @@ namespace amnezia
|
|||
constexpr char defaultJunkPacketMaxSize[] = "30";
|
||||
constexpr char defaultInitPacketJunkSize[] = "15";
|
||||
constexpr char defaultResponsePacketJunkSize[] = "18";
|
||||
constexpr char defaultCookieReplyPacketJunkSize[] = "20";
|
||||
constexpr char defaultTransportPacketJunkSize[] = "23";
|
||||
|
||||
constexpr char defaultInitPacketMagicHeader[] = "1020325451";
|
||||
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[] = "";
|
||||
}
|
||||
|
||||
namespace socks5Proxy
|
||||
|
|
|
@ -98,8 +98,13 @@ ErrorCode XrayProtocol::startTun2Sock()
|
|||
if (vpnState == Vpn::ConnectionState::Connected) {
|
||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||
QList<QHostAddress> dnsAddr;
|
||||
|
||||
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
|
||||
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
|
||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||
if (!m_configData.value(amnezia::config_key::dns1).toString().
|
||||
contains(amnezia::protocols::dns::amneziaDnsIp)) {
|
||||
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
QThread::msleep(8000);
|
||||
#endif
|
||||
|
@ -134,7 +139,7 @@ ErrorCode XrayProtocol::startTun2Sock()
|
|||
// killSwitch toggle
|
||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index());
|
||||
}
|
||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||
|
|
|
@ -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>
|
||||
|
@ -231,6 +236,10 @@
|
|||
<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>
|
||||
<file>ui/qml/Components/AwgTextField.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
|
||||
{
|
||||
|
|
|
@ -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,6 +1,7 @@
|
|||
if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\
|
||||
elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\
|
||||
elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\
|
||||
elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\
|
||||
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
|
||||
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
|
||||
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
|
||||
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
|
||||
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
|
||||
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
|
||||
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi
|
||||
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
|
||||
|
|
|
@ -1,2 +1,14 @@
|
|||
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 zypper > /dev/null 2>&1; then pm=$(which zypper); 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,6 +1,7 @@
|
|||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
|
||||
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
|
||||
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
|
||||
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
|
||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
|
||||
else echo "Packet manager not found"; exit 1; fi;\
|
||||
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
}
|
||||
}
|
101
client/ui/controllers/allowedDnsController.cpp
Normal file
101
client/ui/controllers/allowedDnsController.cpp
Normal file
|
@ -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"));
|
||||
}
|
35
client/ui/controllers/allowedDnsController.h
Normal file
35
client/ui/controllers/allowedDnsController.h
Normal file
|
@ -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
|
|
@ -18,8 +18,9 @@ namespace
|
|||
{
|
||||
constexpr char cloak[] = "cloak";
|
||||
constexpr char awg[] = "awg";
|
||||
constexpr char vless[] = "vless";
|
||||
|
||||
constexpr char apiEdnpoint[] = "api_endpoint";
|
||||
constexpr char apiEndpoint[] = "api_endpoint";
|
||||
constexpr char accessToken[] = "api_key";
|
||||
constexpr char certificate[] = "certificate";
|
||||
constexpr char publicKey[] = "public_key";
|
||||
|
@ -35,10 +36,6 @@ namespace
|
|||
constexpr char serviceInfo[] = "service_info";
|
||||
constexpr char serviceProtocol[] = "service_protocol";
|
||||
|
||||
constexpr char aesKey[] = "aes_key";
|
||||
constexpr char aesIv[] = "aes_iv";
|
||||
constexpr char aesSalt[] = "aes_salt";
|
||||
|
||||
constexpr char apiPayload[] = "api_payload";
|
||||
constexpr char keyPayload[] = "key_payload";
|
||||
|
||||
|
@ -47,6 +44,185 @@ namespace
|
|||
|
||||
constexpr char config[] = "config";
|
||||
}
|
||||
|
||||
struct ProtocolData
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData certRequest;
|
||||
|
||||
QString wireGuardClientPrivKey;
|
||||
QString wireGuardClientPubKey;
|
||||
|
||||
QString xrayUuid;
|
||||
};
|
||||
|
||||
struct GatewayRequestData
|
||||
{
|
||||
QString osVersion;
|
||||
QString appVersion;
|
||||
|
||||
QString installationUuid;
|
||||
|
||||
QString userCountryCode;
|
||||
QString serverCountryCode;
|
||||
QString serviceType;
|
||||
QString serviceProtocol;
|
||||
|
||||
QJsonObject authData;
|
||||
|
||||
QJsonObject toJsonObject() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
if (!osVersion.isEmpty()) {
|
||||
obj[configKey::osVersion] = osVersion;
|
||||
}
|
||||
if (!appVersion.isEmpty()) {
|
||||
obj[configKey::appVersion] = appVersion;
|
||||
}
|
||||
if (!installationUuid.isEmpty()) {
|
||||
obj[configKey::uuid] = installationUuid;
|
||||
}
|
||||
if (!userCountryCode.isEmpty()) {
|
||||
obj[configKey::userCountryCode] = userCountryCode;
|
||||
}
|
||||
if (!serverCountryCode.isEmpty()) {
|
||||
obj[configKey::serverCountryCode] = serverCountryCode;
|
||||
}
|
||||
if (!serviceType.isEmpty()) {
|
||||
obj[configKey::serviceType] = serviceType;
|
||||
}
|
||||
if (!serviceProtocol.isEmpty()) {
|
||||
obj[configKey::serviceProtocol] = serviceProtocol;
|
||||
}
|
||||
if (!authData.isEmpty()) {
|
||||
obj[configKey::authData] = authData;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
ProtocolData generateProtocolData(const QString &protocol)
|
||||
{
|
||||
ProtocolData protocolData;
|
||||
if (protocol == configKey::cloak) {
|
||||
protocolData.certRequest = OpenVpnConfigurator::createCertRequest();
|
||||
} else if (protocol == configKey::awg) {
|
||||
auto connData = WireguardConfigurator::genClientKeys();
|
||||
protocolData.wireGuardClientPubKey = connData.clientPubKey;
|
||||
protocolData.wireGuardClientPrivKey = connData.clientPrivKey;
|
||||
} else if (protocol == configKey::vless) {
|
||||
protocolData.xrayUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
}
|
||||
|
||||
return protocolData;
|
||||
}
|
||||
|
||||
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload)
|
||||
{
|
||||
if (protocol == configKey::cloak) {
|
||||
apiPayload[configKey::certificate] = protocolData.certRequest.request;
|
||||
} else if (protocol == configKey::awg) {
|
||||
apiPayload[configKey::publicKey] = protocolData.wireGuardClientPubKey;
|
||||
} else if (protocol == configKey::vless) {
|
||||
apiPayload[configKey::publicKey] = protocolData.xrayUuid;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode fillServerConfig(const QString &protocol, const ProtocolData &apiPayloadData, const QByteArray &apiResponseBody,
|
||||
QJsonObject &serverConfig)
|
||||
{
|
||||
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
|
||||
|
||||
data.replace("vpn://", "");
|
||||
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
|
||||
if (ba.isEmpty()) {
|
||||
qDebug() << "empty vpn key";
|
||||
return ErrorCode::ApiConfigEmptyError;
|
||||
}
|
||||
|
||||
QByteArray ba_uncompressed = qUncompress(ba);
|
||||
if (!ba_uncompressed.isEmpty()) {
|
||||
ba = ba_uncompressed;
|
||||
}
|
||||
|
||||
QString configStr = ba;
|
||||
if (protocol == configKey::cloak) {
|
||||
configStr.replace("<key>", "<key>\n");
|
||||
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
|
||||
} else if (protocol == configKey::awg) {
|
||||
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
auto containers = newServerConfig.value(config_key::containers).toArray();
|
||||
if (containers.isEmpty()) {
|
||||
qDebug() << "missing containers field";
|
||||
return ErrorCode::ApiConfigEmptyError;
|
||||
}
|
||||
auto container = containers.at(0).toObject();
|
||||
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||
auto serverProtocolConfig = container.value(containerName).toObject();
|
||||
auto clientProtocolConfig =
|
||||
QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||
|
||||
//TODO looks like this block can be removed after v1 configs EOL
|
||||
|
||||
serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount);
|
||||
serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize);
|
||||
serverProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize);
|
||||
serverProtocolConfig[config_key::initPacketJunkSize] = clientProtocolConfig.value(config_key::initPacketJunkSize);
|
||||
serverProtocolConfig[config_key::responsePacketJunkSize] = clientProtocolConfig.value(config_key::responsePacketJunkSize);
|
||||
serverProtocolConfig[config_key::initPacketMagicHeader] = clientProtocolConfig.value(config_key::initPacketMagicHeader);
|
||||
serverProtocolConfig[config_key::responsePacketMagicHeader] = clientProtocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
serverProtocolConfig[config_key::underloadPacketMagicHeader] = clientProtocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
serverProtocolConfig[config_key::transportPacketMagicHeader] = clientProtocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
|
||||
serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = clientProtocolConfig.value(config_key::cookieReplyPacketJunkSize);
|
||||
serverProtocolConfig[config_key::transportPacketJunkSize] = clientProtocolConfig.value(config_key::transportPacketJunkSize);
|
||||
serverProtocolConfig[config_key::specialJunk1] = clientProtocolConfig.value(config_key::specialJunk1);
|
||||
serverProtocolConfig[config_key::specialJunk2] = clientProtocolConfig.value(config_key::specialJunk2);
|
||||
serverProtocolConfig[config_key::specialJunk3] = clientProtocolConfig.value(config_key::specialJunk3);
|
||||
serverProtocolConfig[config_key::specialJunk4] = clientProtocolConfig.value(config_key::specialJunk4);
|
||||
serverProtocolConfig[config_key::specialJunk5] = clientProtocolConfig.value(config_key::specialJunk5);
|
||||
serverProtocolConfig[config_key::controlledJunk1] = clientProtocolConfig.value(config_key::controlledJunk1);
|
||||
serverProtocolConfig[config_key::controlledJunk2] = clientProtocolConfig.value(config_key::controlledJunk2);
|
||||
serverProtocolConfig[config_key::controlledJunk3] = clientProtocolConfig.value(config_key::controlledJunk3);
|
||||
serverProtocolConfig[config_key::specialHandshakeTimeout] = clientProtocolConfig.value(config_key::specialHandshakeTimeout);
|
||||
|
||||
//
|
||||
|
||||
container[containerName] = serverProtocolConfig;
|
||||
containers.replace(0, container);
|
||||
newServerConfig[config_key::containers] = containers;
|
||||
configStr = QString(QJsonDocument(newServerConfig).toJson());
|
||||
}
|
||||
|
||||
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
|
||||
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
|
||||
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
|
||||
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
|
||||
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
|
||||
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
|
||||
}
|
||||
|
||||
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||
|
||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
apiConfig.insert(apiDefs::key::supportedProtocols,
|
||||
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
}
|
||||
|
||||
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
|
||||
|
@ -63,22 +239,26 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
|||
return false;
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
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);
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ProtocolData protocolData = generateProtocolData(protocol);
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
|
@ -86,7 +266,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
|||
|
||||
QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object();
|
||||
QString nativeConfig = jsonConfig.value(configKey::config).toString();
|
||||
nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||
nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey);
|
||||
|
||||
SystemController::saveFile(fileName, nativeConfig);
|
||||
return true;
|
||||
|
@ -94,22 +274,22 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
|||
|
||||
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
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);
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
|
@ -140,13 +320,11 @@ void ApiConfigsController::copyVpnKeyToClipboard()
|
|||
|
||||
bool ApiConfigsController::fillAvailableServices()
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
if (!responseBody.contains("services")) {
|
||||
errorCode = ErrorCode::ApiServicesMissingError;
|
||||
|
@ -165,32 +343,36 @@ bool ApiConfigsController::fillAvailableServices()
|
|||
|
||||
bool ApiConfigsController::importServiceFromGateway()
|
||||
{
|
||||
if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
|
||||
m_apiServicesModel->getSelectedServiceProtocol())) {
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
m_apiServicesModel->getCountryCode(),
|
||||
"",
|
||||
m_apiServicesModel->getSelectedServiceType(),
|
||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||
QJsonObject() };
|
||||
|
||||
if (m_serversModel->isServerFromApiAlreadyExists(gatewayRequestData.userCountryCode, gatewayRequestData.serviceType,
|
||||
gatewayRequestData.serviceProtocol)) {
|
||||
emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
|
||||
return false;
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol);
|
||||
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
auto userCountryCode = m_apiServicesModel->getCountryCode();
|
||||
auto serviceType = m_apiServicesModel->getSelectedServiceType();
|
||||
auto serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
|
||||
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
|
||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||
apiPayload[configKey::serviceType] = serviceType;
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
|
||||
QJsonObject serverConfig;
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
|
||||
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, serverConfig);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
|
||||
|
@ -211,37 +393,33 @@ 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);
|
||||
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
auto authData = serverConfig.value(configKey::authData).toObject();
|
||||
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
auto userCountryCode = apiConfig.value(configKey::userCountryCode).toString();
|
||||
auto serviceType = apiConfig.value(configKey::serviceType).toString();
|
||||
auto serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfig.value(configKey::userCountryCode).toString(),
|
||||
newCountryCode,
|
||||
apiConfig.value(configKey::serviceType).toString(),
|
||||
apiConfig.value(configKey::serviceProtocol).toString(),
|
||||
serverConfig.value(configKey::authData).toObject() };
|
||||
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
|
||||
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
|
||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||
apiPayload[configKey::serviceType] = serviceType;
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
|
||||
if (!newCountryCode.isEmpty()) {
|
||||
apiPayload[configKey::serverCountryCode] = newCountryCode;
|
||||
}
|
||||
if (!authData.isEmpty()) {
|
||||
apiPayload[configKey::authData] = authData;
|
||||
}
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
|
||||
QJsonObject newServerConfig;
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig);
|
||||
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, newServerConfig);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
|
||||
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
|
||||
|
@ -250,9 +428,12 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
|||
newApiConfig.insert(apiDefs::key::vpnKey, apiConfig.value(apiDefs::key::vpnKey));
|
||||
|
||||
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
||||
newServerConfig.insert(configKey::authData, authData);
|
||||
// newServerConfig.insert(
|
||||
newServerConfig.insert(configKey::authData, gatewayRequestData.authData);
|
||||
|
||||
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
|
||||
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
|
||||
newServerConfig.insert(config_key::nameOverriddenByUser, true);
|
||||
}
|
||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||
if (reloadServiceConfig) {
|
||||
emit reloadServerFromApiFinished(tr("API config reloaded"));
|
||||
|
@ -270,80 +451,70 @@ 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();
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
QJsonObject apiPayload;
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
apiPayload[configKey::appVersion] = QString(APP_VERSION);
|
||||
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);
|
||||
|
||||
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) {
|
||||
errorCode = fillServerConfig(serviceProtocol, protocolData, responseBody, serverConfig);
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
apiConfigObject.value(configKey::serverCountryCode).toString(),
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
"",
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
|
||||
apiPayload[configKey::serverCountryCode] = apiConfigObject.value(configKey::serverCountryCode);
|
||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
|
@ -357,28 +528,27 @@ bool ApiConfigsController::deactivateDevice()
|
|||
|
||||
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
uuid,
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
"",
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
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;
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
|
@ -417,108 +587,29 @@ bool ApiConfigsController::isConfigValid()
|
|||
return true;
|
||||
}
|
||||
|
||||
ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol)
|
||||
void ApiConfigsController::setCurrentProtocol(const QString &protocolName)
|
||||
{
|
||||
ApiConfigsController::ApiPayloadData apiPayload;
|
||||
if (protocol == configKey::cloak) {
|
||||
apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
|
||||
} else if (protocol == configKey::awg) {
|
||||
auto connData = WireguardConfigurator::genClientKeys();
|
||||
apiPayload.wireGuardClientPubKey = connData.clientPubKey;
|
||||
apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
|
||||
}
|
||||
return apiPayload;
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
apiConfigObject[configKey::serviceProtocol] = protocolName;
|
||||
|
||||
serverConfigObject.insert(configKey::apiConfig, apiConfigObject);
|
||||
|
||||
m_serversModel->editServer(serverConfigObject, serverIndex);
|
||||
}
|
||||
|
||||
QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData)
|
||||
bool ApiConfigsController::isVlessProtocol()
|
||||
{
|
||||
QJsonObject obj;
|
||||
if (protocol == configKey::cloak) {
|
||||
obj[configKey::certificate] = apiPayloadData.certRequest.request;
|
||||
} else if (protocol == configKey::awg) {
|
||||
obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (apiConfigObject[configKey::serviceProtocol].toString() == "vless") {
|
||||
return true;
|
||||
}
|
||||
|
||||
obj[configKey::osVersion] = QSysInfo::productType();
|
||||
obj[configKey::appVersion] = QString(APP_VERSION);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData,
|
||||
const QByteArray &apiResponseBody, QJsonObject &serverConfig)
|
||||
{
|
||||
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
|
||||
|
||||
data.replace("vpn://", "");
|
||||
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
|
||||
if (ba.isEmpty()) {
|
||||
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray ba_uncompressed = qUncompress(ba);
|
||||
if (!ba_uncompressed.isEmpty()) {
|
||||
ba = ba_uncompressed;
|
||||
}
|
||||
|
||||
QString configStr = ba;
|
||||
if (protocol == configKey::cloak) {
|
||||
configStr.replace("<key>", "<key>\n");
|
||||
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
|
||||
} else if (protocol == configKey::awg) {
|
||||
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
auto containers = newServerConfig.value(config_key::containers).toArray();
|
||||
if (containers.isEmpty()) {
|
||||
return; // todo process error
|
||||
}
|
||||
auto container = containers.at(0).toObject();
|
||||
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||
auto containerConfig = container.value(containerName).toObject();
|
||||
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
|
||||
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
|
||||
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
|
||||
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
|
||||
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
|
||||
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
|
||||
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
container[containerName] = containerConfig;
|
||||
containers.replace(0, container);
|
||||
newServerConfig[config_key::containers] = containers;
|
||||
configStr = QString(QJsonDocument(newServerConfig).toJson());
|
||||
}
|
||||
|
||||
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
|
||||
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
|
||||
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
|
||||
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
|
||||
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
|
||||
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
|
||||
}
|
||||
|
||||
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||
|
||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QString> ApiConfigsController::getQrCodes()
|
||||
|
@ -535,3 +626,10 @@ QString ApiConfigsController::getVpnKey()
|
|||
{
|
||||
return m_vpnKey;
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
return gatewayController.post(endpoint, apiPayload, responseBody);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ public slots:
|
|||
|
||||
bool isConfigValid();
|
||||
|
||||
void setCurrentProtocol(const QString &protocolName);
|
||||
bool isVlessProtocol();
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
|
||||
|
@ -46,23 +49,12 @@ signals:
|
|||
void vpnKeyExportReady();
|
||||
|
||||
private:
|
||||
struct ApiPayloadData
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData certRequest;
|
||||
|
||||
QString wireGuardClientPrivKey;
|
||||
QString wireGuardClientPubKey;
|
||||
};
|
||||
|
||||
ApiPayloadData generateApiPayloadData(const QString &protocol);
|
||||
QJsonObject fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData);
|
||||
void fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
|
||||
QJsonObject &serverConfig);
|
||||
|
||||
QList<QString> getQrCodes();
|
||||
int getQrCodesCount();
|
||||
QString getVpnKey();
|
||||
|
||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
|
||||
|
||||
QList<QString> m_qrCodes;
|
||||
QString m_vpnKey;
|
||||
|
||||
|
|
133
client/ui/controllers/api/apiPremV1MigrationController.cpp
Normal file
133
client/ui/controllers/api/apiPremV1MigrationController.cpp
Normal file
|
@ -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();
|
||||
}
|
50
client/ui/controllers/api/apiPremV1MigrationController.h
Normal file
50
client/ui/controllers/api/apiPremV1MigrationController.h
Normal file
|
@ -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
|
||||
{
|
||||
|
@ -48,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);
|
||||
|
@ -59,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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "core/errorstrings.h"
|
||||
#include "core/qrCodeUtils.h"
|
||||
#include "core/serialization/serialization.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "systemController.h"
|
||||
#include "utilities.h"
|
||||
|
||||
|
@ -27,8 +28,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 +52,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 +95,8 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
|
|||
|
||||
bool ImportController::extractConfigFromData(QString data)
|
||||
{
|
||||
m_maliciousWarningText.clear();
|
||||
|
||||
QString config = data;
|
||||
QString prefix;
|
||||
QString errormsg;
|
||||
|
@ -287,6 +287,19 @@ void ImportController::processNativeWireGuardConfig()
|
|||
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3";
|
||||
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4";
|
||||
|
||||
// clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
|
||||
// clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
|
||||
|
||||
// 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;
|
||||
|
||||
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
|
||||
|
@ -345,7 +358,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);
|
||||
|
@ -439,21 +452,33 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
|||
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
|
||||
|
||||
QString protocolName = "wireguard";
|
||||
if (!configMap.value(config_key::junkPacketCount).isEmpty() && !configMap.value(config_key::junkPacketMinSize).isEmpty()
|
||||
&& !configMap.value(config_key::junkPacketMaxSize).isEmpty() && !configMap.value(config_key::initPacketJunkSize).isEmpty()
|
||||
&& !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()) {
|
||||
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);
|
||||
lastConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
|
||||
lastConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
|
||||
lastConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
|
||||
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);
|
||||
|
||||
const QStringList requiredJunkFields = { config_key::junkPacketCount, config_key::junkPacketMinSize,
|
||||
config_key::junkPacketMaxSize, config_key::initPacketJunkSize,
|
||||
config_key::responsePacketJunkSize, config_key::initPacketMagicHeader,
|
||||
config_key::responsePacketMagicHeader, config_key::underloadPacketMagicHeader,
|
||||
config_key::transportPacketMagicHeader };
|
||||
|
||||
const QStringList optionalJunkFields = { // config_key::cookieReplyPacketJunkSize,
|
||||
// config_key::transportPacketJunkSize,
|
||||
config_key::specialJunk1, config_key::specialJunk2, config_key::specialJunk3,
|
||||
config_key::specialJunk4, config_key::specialJunk5, config_key::controlledJunk1,
|
||||
config_key::controlledJunk2, config_key::controlledJunk3, config_key::specialHandshakeTimeout
|
||||
};
|
||||
|
||||
bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(),
|
||||
[&configMap](const QString &field) { return !configMap.value(field).isEmpty(); });
|
||||
if (hasAllRequiredFields) {
|
||||
for (const QString &field : requiredJunkFields) {
|
||||
lastConfig[field] = configMap.value(field);
|
||||
}
|
||||
|
||||
for (const QString &field : optionalJunkFields) {
|
||||
if (!configMap.value(field).isEmpty()) {
|
||||
lastConfig[field] = configMap.value(field);
|
||||
}
|
||||
}
|
||||
|
||||
protocolName = "awg";
|
||||
m_configType = ConfigTypes::Awg;
|
||||
}
|
||||
|
@ -661,29 +686,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));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QStandardPaths>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
@ -15,7 +16,6 @@
|
|||
#include "ui/models/protocols/awgConfigModel.h"
|
||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||
#include "utilities.h"
|
||||
#include "core/api/apiUtils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -79,12 +79,36 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
|||
|
||||
int s1 = QRandomGenerator::global()->bounded(15, 150);
|
||||
int s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||
while (s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
|
||||
// int s3 = QRandomGenerator::global()->bounded(15, 150);
|
||||
// int s4 = QRandomGenerator::global()->bounded(15, 150);
|
||||
|
||||
// Ensure all values are unique and don't create equal packet sizes
|
||||
QSet<int> usedValues;
|
||||
usedValues.insert(s1);
|
||||
|
||||
while (usedValues.contains(s2) || s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
|
||||
s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||
}
|
||||
usedValues.insert(s2);
|
||||
|
||||
// while (usedValues.contains(s3)
|
||||
// || s1 + AwgConstant::messageInitiationSize == s3 + AwgConstant::messageCookieReplySize
|
||||
// || s2 + AwgConstant::messageResponseSize == s3 + AwgConstant::messageCookieReplySize) {
|
||||
// s3 = QRandomGenerator::global()->bounded(15, 150);
|
||||
// }
|
||||
// usedValues.insert(s3);
|
||||
|
||||
// while (usedValues.contains(s4)
|
||||
// || s1 + AwgConstant::messageInitiationSize == s4 + AwgConstant::messageTransportSize
|
||||
// || s2 + AwgConstant::messageResponseSize == s4 + AwgConstant::messageTransportSize
|
||||
// || s3 + AwgConstant::messageCookieReplySize == s4 + AwgConstant::messageTransportSize) {
|
||||
// s4 = QRandomGenerator::global()->bounded(15, 150);
|
||||
// }
|
||||
|
||||
QString initPacketJunkSize = QString::number(s1);
|
||||
QString responsePacketJunkSize = QString::number(s2);
|
||||
// QString cookieReplyPacketJunkSize = QString::number(s3);
|
||||
// QString transportPacketJunkSize = QString::number(s4);
|
||||
|
||||
QSet<QString> headersValue;
|
||||
while (headersValue.size() != 4) {
|
||||
|
@ -108,6 +132,21 @@ 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::cookieReplyPacketJunkSize] = cookieReplyPacketJunkSize;
|
||||
// containerConfig[config_key::transportPacketJunkSize] = transportPacketJunkSize;
|
||||
|
||||
// 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 +402,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 +427,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 +439,38 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
|||
serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||
containerConfig[config_key::transportPacketMagicHeader] =
|
||||
serverConfigMap.value(config_key::transportPacketMagicHeader);
|
||||
|
||||
// containerConfig[config_key::cookieReplyPacketJunkSize] = serverConfigMap.value(config_key::cookieReplyPacketJunkSize);
|
||||
// containerConfig[config_key::transportPacketJunkSize] = serverConfigMap.value(config_key::transportPacketJunkSize);
|
||||
|
||||
// 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 +505,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,13 +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"));
|
||||
}
|
||||
|
|
86
client/ui/models/allowed_dns_model.cpp
Normal file
86
client/ui/models/allowed_dns_model.cpp
Normal file
|
@ -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();
|
||||
}
|
37
client/ui/models/allowed_dns_model.h
Normal file
37
client/ui/models/allowed_dns_model.h
Normal file
|
@ -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,19 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
case ServiceDescriptionRole: {
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. "
|
||||
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++) {
|
||||
|
@ -71,6 +75,12 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
return false;
|
||||
}
|
||||
case IsProtocolSelectionSupportedRole: {
|
||||
if (m_accountInfoData.supportedProtocols.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -91,8 +101,14 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
|||
|
||||
accountInfoData.configType = apiUtils::getConfigType(serverConfig);
|
||||
|
||||
for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) {
|
||||
accountInfoData.supportedProtocols.push_back(protocol.toString());
|
||||
}
|
||||
|
||||
m_accountInfoData = accountInfoData;
|
||||
|
||||
m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
@ -121,12 +137,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
|
||||
|
@ -138,6 +169,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
|||
roles[ServiceDescriptionRole] = "serviceDescription";
|
||||
roles[IsComponentVisibleRole] = "isComponentVisible";
|
||||
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
||||
roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ public:
|
|||
ServiceDescriptionRole,
|
||||
EndDateRole,
|
||||
IsComponentVisibleRole,
|
||||
HasExpiredWorkerRole
|
||||
HasExpiredWorkerRole,
|
||||
IsProtocolSelectionSupportedRole
|
||||
};
|
||||
|
||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||
|
@ -33,7 +34,12 @@ public slots:
|
|||
|
||||
QJsonArray getAvailableCountries();
|
||||
QJsonArray getIssuedConfigsInfo();
|
||||
|
||||
QString getTelegramBotLink();
|
||||
QString getEmailLink();
|
||||
QString getBillingEmailLink();
|
||||
QString getSiteLink();
|
||||
QString getFullSiteLink();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
@ -46,11 +52,14 @@ private:
|
|||
int maxDeviceCount;
|
||||
|
||||
apiDefs::ConfigType configType;
|
||||
|
||||
QStringList supportedProtocols;
|
||||
};
|
||||
|
||||
AccountInfoData m_accountInfoData;
|
||||
QJsonArray m_availableCountries;
|
||||
QJsonArray m_issuedConfigsInfo;
|
||||
QJsonObject m_supportInfo;
|
||||
};
|
||||
|
||||
#endif // APIACCOUNTINFOMODEL_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);
|
||||
|
|
|
@ -28,7 +28,17 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
|
|||
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;
|
||||
|
@ -36,6 +46,12 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
|
|||
case Roles::ServerResponsePacketJunkSizeRole:
|
||||
m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString());
|
||||
break;
|
||||
// case Roles::ServerCookieReplyPacketJunkSizeRole:
|
||||
// m_serverProtocolConfig.insert(config_key::cookieReplyPacketJunkSize, value.toString());
|
||||
// break;
|
||||
// case Roles::ServerTransportPacketJunkSizeRole:
|
||||
// m_serverProtocolConfig.insert(config_key::transportPacketJunkSize, 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());
|
||||
|
@ -66,12 +82,23 @@ 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::ServerCookieReplyPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize);
|
||||
// case Roles::ServerTransportPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::transportPacketJunkSize);
|
||||
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);
|
||||
|
@ -94,7 +121,8 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
|||
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::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);
|
||||
|
@ -106,6 +134,10 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
|||
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::responsePacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
// m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
|
||||
// serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
|
||||
// m_serverProtocolConfig[config_key::transportPacketJunkSize] =
|
||||
// serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::initPacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
m_serverProtocolConfig[config_key::responsePacketMagicHeader] =
|
||||
|
@ -124,6 +156,24 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
|||
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());
|
||||
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 +191,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());
|
||||
}
|
||||
|
@ -159,6 +218,17 @@ bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2)
|
|||
return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2);
|
||||
}
|
||||
|
||||
// bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2, const int s3, const int s4)
|
||||
// {
|
||||
// int initSize = AwgConstant::messageInitiationSize + s1;
|
||||
// int responseSize = AwgConstant::messageResponseSize + s2;
|
||||
// int cookieSize = AwgConstant::messageCookieReplySize + s3;
|
||||
// int transportSize = AwgConstant::messageTransportSize + s4;
|
||||
|
||||
// return (initSize == responseSize || initSize == cookieSize || initSize == transportSize || responseSize == cookieSize
|
||||
// || responseSize == transportSize || cookieSize == transportSize);
|
||||
// }
|
||||
|
||||
bool AwgConfigModel::isServerSettingsEqual()
|
||||
{
|
||||
const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject());
|
||||
|
@ -178,12 +248,24 @@ 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";
|
||||
roles[ServerJunkPacketMaxSizeRole] = "serverJunkPacketMaxSize";
|
||||
roles[ServerInitPacketJunkSizeRole] = "serverInitPacketJunkSize";
|
||||
roles[ServerResponsePacketJunkSizeRole] = "serverResponsePacketJunkSize";
|
||||
roles[ServerCookieReplyPacketJunkSizeRole] = "serverCookieReplyPacketJunkSize";
|
||||
roles[ServerTransportPacketJunkSizeRole] = "serverTransportPacketJunkSize";
|
||||
|
||||
roles[ServerInitPacketMagicHeaderRole] = "serverInitPacketMagicHeader";
|
||||
roles[ServerResponsePacketMagicHeaderRole] = "serverResponsePacketMagicHeader";
|
||||
roles[ServerUnderloadPacketMagicHeaderRole] = "serverUnderloadPacketMagicHeader";
|
||||
|
@ -200,6 +282,16 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
|
|||
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);
|
||||
port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||
|
@ -209,6 +301,10 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
|
|||
serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
serverResponsePacketJunkSize =
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
// serverCookieReplyPacketJunkSize =
|
||||
// serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
|
||||
// serverTransportPacketJunkSize =
|
||||
// serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
|
||||
serverInitPacketMagicHeader =
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
serverResponsePacketMagicHeader =
|
||||
|
@ -224,6 +320,8 @@ 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
|
||||
// || serverCookieReplyPacketJunkSize != other.serverCookieReplyPacketJunkSize
|
||||
// || serverTransportPacketJunkSize != other.serverTransportPacketJunkSize
|
||||
|| serverInitPacketMagicHeader != other.serverInitPacketMagicHeader
|
||||
|| serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader
|
||||
|| serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader
|
||||
|
@ -236,7 +334,12 @@ 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;
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
#include "containers/containers_defs.h"
|
||||
|
||||
namespace AwgConstant {
|
||||
namespace AwgConstant
|
||||
{
|
||||
const int messageInitiationSize = 148;
|
||||
const int messageResponseSize = 92;
|
||||
const int messageCookieReplySize = 64;
|
||||
const int messageTransportSize = 32;
|
||||
}
|
||||
|
||||
struct AwgConfig
|
||||
|
@ -22,12 +25,23 @@ 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;
|
||||
QString serverJunkPacketMaxSize;
|
||||
QString serverInitPacketJunkSize;
|
||||
QString serverResponsePacketJunkSize;
|
||||
QString serverCookieReplyPacketJunkSize;
|
||||
QString serverTransportPacketJunkSize;
|
||||
QString serverInitPacketMagicHeader;
|
||||
QString serverResponsePacketMagicHeader;
|
||||
QString serverUnderloadPacketMagicHeader;
|
||||
|
@ -35,7 +49,6 @@ struct AwgConfig
|
|||
|
||||
bool hasEqualServerSettings(const AwgConfig &other) const;
|
||||
bool hasEqualClientSettings(const AwgConfig &other) const;
|
||||
|
||||
};
|
||||
|
||||
class AwgConfigModel : public QAbstractListModel
|
||||
|
@ -51,16 +64,28 @@ public:
|
|||
ClientJunkPacketCountRole,
|
||||
ClientJunkPacketMinSizeRole,
|
||||
ClientJunkPacketMaxSizeRole,
|
||||
ClientSpecialJunk1Role,
|
||||
ClientSpecialJunk2Role,
|
||||
ClientSpecialJunk3Role,
|
||||
ClientSpecialJunk4Role,
|
||||
ClientSpecialJunk5Role,
|
||||
ClientControlledJunk1Role,
|
||||
ClientControlledJunk2Role,
|
||||
ClientControlledJunk3Role,
|
||||
ClientSpecialHandshakeTimeoutRole,
|
||||
|
||||
ServerJunkPacketCountRole,
|
||||
ServerJunkPacketMinSizeRole,
|
||||
ServerJunkPacketMaxSizeRole,
|
||||
ServerInitPacketJunkSizeRole,
|
||||
ServerResponsePacketJunkSizeRole,
|
||||
ServerCookieReplyPacketJunkSizeRole,
|
||||
ServerTransportPacketJunkSizeRole,
|
||||
|
||||
ServerInitPacketMagicHeaderRole,
|
||||
ServerResponsePacketMagicHeaderRole,
|
||||
ServerUnderloadPacketMagicHeaderRole,
|
||||
ServerTransportPacketMagicHeaderRole
|
||||
ServerTransportPacketMagicHeaderRole,
|
||||
};
|
||||
|
||||
explicit AwgConfigModel(QObject *parent = nullptr);
|
||||
|
@ -75,7 +100,7 @@ public slots:
|
|||
QJsonObject getConfig();
|
||||
|
||||
bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4);
|
||||
bool isPacketSizeEqual(const int s1, const int s2);
|
||||
bool isPacketSizeEqual(const int s1, const int s2/*, const int s3, const int s4*/);
|
||||
|
||||
bool isServerSettingsEqual();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <AmneziaVPN-Swift.h>
|
||||
#endif
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
|
@ -66,6 +68,7 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
|
|||
} else {
|
||||
server.insert(config_key::description, value.toString());
|
||||
}
|
||||
server.insert(config_key::nameOverriddenByUser, true);
|
||||
m_settings->editServer(index.row(), server);
|
||||
m_servers.replace(index.row(), server);
|
||||
if (index.row() == m_defaultServerIndex) {
|
||||
|
@ -348,6 +351,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;
|
||||
|
@ -407,7 +429,7 @@ void ServersModel::updateDefaultServerContainersModel()
|
|||
emit defaultServerContainersUpdated(containers);
|
||||
}
|
||||
|
||||
QJsonObject ServersModel::getServerConfig(const int serverIndex)
|
||||
QJsonObject ServersModel::getServerConfig(const int serverIndex) const
|
||||
{
|
||||
return m_servers.at(serverIndex).toObject();
|
||||
}
|
||||
|
@ -794,3 +816,8 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
|
|||
}
|
||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
|
||||
}
|
||||
|
||||
bool ServersModel::processedServerIsPremium() const
|
||||
{
|
||||
return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex));
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ public:
|
|||
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
|
||||
|
||||
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
||||
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged)
|
||||
|
||||
bool processedServerIsPremium() const;
|
||||
|
||||
public slots:
|
||||
void setDefaultServerIndex(const int index);
|
||||
|
@ -90,8 +93,9 @@ 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);
|
||||
QJsonObject getServerConfig(const int serverIndex) const;
|
||||
|
||||
void reloadDefaultServerContainerConfig();
|
||||
void updateContainerConfig(const int containerIndex, const QJsonObject config);
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue