mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
Compare commits
224 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb2d0491be | |||
| d38c7ce6a5 | |||
| ef0530ec6b | |||
| a3e4d89a86 | |||
| ed10774eed | |||
| e8a2e54d05 | |||
| dd7362ad6f | |||
| e9f6b95853 | |||
| b4694313a0 | |||
| abb2cae1f8 | |||
| b0004fd9dc | |||
| 362a82f944 | |||
| 19fe61ed29 | |||
| 6a03eead3f | |||
| 9d456dc18e | |||
| 72de38b4fb | |||
| 02c0f96e5e | |||
| 5e9f688000 | |||
| 6a7e346695 | |||
| 071738116e | |||
| ae4ee6431d | |||
| d1ccde2a4b | |||
| 4848091203 | |||
| 282f159311 | |||
| 4ef8c77a2d | |||
| 08c1cf2439 | |||
| 2fc33875bb | |||
| 9e92e4b5ff | |||
| 7f2cf70bf5 | |||
| 8164026891 | |||
| 0e23b3a1ac | |||
| 1739d4861e | |||
| a6b6e7850d | |||
| 3e9dea6f07 | |||
| 1b37ca805f | |||
| c772f56da7 | |||
| bc183e39bb | |||
| 306d4f70a8 | |||
| a386d39495 | |||
| 22b14dff5f | |||
| e749cc7578 | |||
| 6a12cad1c9 | |||
| c15665803d | |||
| 97090888d5 | |||
| 4642308fbb | |||
| 59bccb1188 | |||
| cd8fc007ac | |||
| 7cfb38307e | |||
| 994aa32745 | |||
| f0b872e86b | |||
| 0c33432436 | |||
| 0bb4dd9442 | |||
| 7a54dc15da | |||
| e16a1100d8 | |||
| 99214e22e3 | |||
| c77d35a2ed | |||
| d98fdbdc5c | |||
| 4551cf0a21 | |||
| 023c3474d2 | |||
| 2a4cefb4bf | |||
| 09305724fa | |||
| 360fda1ba7 | |||
| dadf0cf96e | |||
| 3d60ac751e | |||
| 32793eef8c | |||
| da1cdfd6fa | |||
| 58ad7dc161 | |||
| 0a15f44193 | |||
| e1dec3c1ba | |||
| 7834860245 | |||
| 2da1025f26 | |||
| 78c83b2e21 | |||
| 414a47e2f2 | |||
| 6c78b4ec8f | |||
| a6949bd3ae | |||
| f7bed04ab2 | |||
| 6ec773079c | |||
| 366e27a321 | |||
| 32c304dc1b | |||
| 338499247d | |||
| 79e1761c1f | |||
| 4ea1a19572 | |||
| e2ae341ba9 | |||
| de03435bac | |||
| e16c425f87 | |||
| c461e00c5c | |||
| fcf6bb43b7 | |||
| f5f72f87a6 | |||
| 3340451245 | |||
| c14f1b5000 | |||
| a46e55d5c2 | |||
| 4b64bfaec0 | |||
| 2f0c1eeecc | |||
| 546d4c1d3d | |||
| 160d88f002 | |||
| a83cd29f72 | |||
| 94304b5777 | |||
| 61ddfe01a1 | |||
| 00d334f704 | |||
| f4a4979997 | |||
| 03171e4743 | |||
| 5369e68267 | |||
| 9eb23e38bd | |||
| 2a0166bb26 | |||
| 2df612ec1f | |||
| 36ba3758db | |||
| 7cc0f39d3c | |||
| 9cf5590371 | |||
| 81f835458f | |||
| e01b1db706 | |||
| cdb18de305 | |||
| 8e0eef3316 | |||
| 221d45f564 | |||
| 2a4a01a4be | |||
| 501670bdd2 | |||
| 24637a1693 | |||
| 7bd1340190 | |||
| cb5c09d967 | |||
| a01ba5909c | |||
| 1c4678af95 | |||
| 5f5435c645 | |||
| 2f7dc2c46c | |||
| 9bc1c9dd03 | |||
| 4c81cdb4a2 | |||
| 3406ffa7a2 | |||
| 6d05b6845e | |||
| 29b4966119 | |||
| d0f8358431 | |||
| a75bd07cd8 | |||
| 8c1835950b | |||
| c7cd8e4c80 | |||
| f65e4066e3 | |||
| 37c18c5d3c | |||
| 8885f580b2 | |||
| 512ac74ee6 | |||
| 384ce9853b | |||
| b6d2030041 | |||
| 3ac09181c6 | |||
| ffc9e5823a | |||
| f5448fed59 | |||
| 8163e51434 | |||
| 3836836c72 | |||
| b78bf39767 | |||
| 7c8399ce88 | |||
| 9fe2a1dd41 | |||
| 846f554157 | |||
| d1f66cbf4d | |||
| a4624c7377 | |||
| 10435cea69 | |||
| ce9a23e021 | |||
| 4b7c8f21c2 | |||
| 6ddebdbbd1 | |||
| d92729d346 | |||
| fa06dbbd29 | |||
| 5d59a1a10e | |||
| 222a251180 | |||
| 9d6559f0d7 | |||
| da02f49850 | |||
| 992961c488 | |||
| 0ce30a4e81 | |||
| bb2d794b6f | |||
| 45dc302de4 | |||
| 4a2706a9d9 | |||
| 00be3c3ccc | |||
| cb7fe50d46 | |||
| d364dbac2c | |||
| 042788bec3 | |||
| def261f578 | |||
| c08e23085e | |||
| e01dd2bf57 | |||
| 61396ec82e | |||
| 357c283437 | |||
| 1b38cd6ca7 | |||
| bdfa8bfe5b | |||
| 7f2ef65fe6 | |||
| 102d0472c7 | |||
| 445fc6efb1 | |||
| 135726f177 | |||
| 671ca0a66f | |||
| aa4a79934a | |||
| 16fc0617e4 | |||
| 64a2f3f8bb | |||
| b7a65343af | |||
| 5c121ea48d | |||
| 673f28ed64 | |||
| 3fb97d16bb | |||
| 079c9176ef | |||
| 9377a0b545 | |||
| 1357c4a309 | |||
| d77be5a244 | |||
| 08863edb52 | |||
| 3a77705142 | |||
| 1eafa9a38a | |||
| 396b7aac18 | |||
| 08defbbbd8 | |||
| 79d371fb76 | |||
| 9df262d502 | |||
| 6f392ce126 | |||
| a83ec10b61 | |||
| a93f75fb5a | |||
| 2353cc4f2c | |||
| 30709c66ef | |||
| 27f770604b | |||
| 6dbbf1fc89 | |||
| c254f2fdc4 | |||
| cf450fa4e4 | |||
| 304f29bfac | |||
| 50b8b3d649 | |||
| 39c2124a26 | |||
| eaede032b4 | |||
| 4ed153373f | |||
| 07d7fac490 | |||
| 5535b6a6e3 | |||
| b7fbb84a58 | |||
| 19bd94ed02 | |||
| 54b45a36e1 | |||
| 2986a18c8f | |||
| 68095700a2 | |||
| 4cb871849b | |||
| 423305c35a | |||
| b55313527e | |||
| af53c456ea | |||
| 7284bb54bc | |||
| 6afdd8375d |
@@ -1,7 +1,12 @@
|
|||||||
name: 'Deploy workflow'
|
name: 'Deploy workflow'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
on: [push]
|
env:
|
||||||
|
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-Linux-Ubuntu:
|
Build-Linux-Ubuntu:
|
||||||
@@ -25,7 +30,7 @@ jobs:
|
|||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
tools: 'tools_ifw'
|
tools: 'tools_ifw'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -89,7 +94,7 @@ jobs:
|
|||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
tools: 'tools_ifw'
|
tools: 'tools_ifw'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Setup mvsc'
|
- name: 'Setup mvsc'
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
@@ -119,15 +124,14 @@ jobs:
|
|||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-IOS:
|
Build-iOS:
|
||||||
name: 'Build-IOS'
|
name: 'Build-iOS'
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.5.2
|
QT_VERSION: 6.5.2
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Just select XCode
|
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
@@ -143,6 +147,7 @@ jobs:
|
|||||||
arch: 'clang_64'
|
arch: 'clang_64'
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Install iOS Qt'
|
- name: 'Install iOS Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -154,7 +159,7 @@ jobs:
|
|||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Install go'
|
- name: 'Install go'
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
@@ -174,7 +179,7 @@ jobs:
|
|||||||
- name: 'Setup ccache'
|
- name: 'Setup ccache'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: 'Install dependencies'
|
||||||
run: pip install jsonschema jinja2
|
run: pip install jsonschema jinja2
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
@@ -232,7 +237,7 @@ jobs:
|
|||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
tools: 'tools_ifw'
|
tools: 'tools_ifw'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -296,7 +301,7 @@ jobs:
|
|||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Install android Qt'
|
- name: 'Install android Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -309,7 +314,7 @@ jobs:
|
|||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
setup-python: 'true'
|
setup-python: 'true'
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Grant execute permission for qt-cmake'
|
- name: 'Grant execute permission for qt-cmake'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
name: 'Upload a new version'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: upload
|
||||||
|
steps:
|
||||||
|
- name: Checkout CMakeLists.txt
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref_name }}
|
||||||
|
sparse-checkout: |
|
||||||
|
CMakeLists.txt
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
|
- name: Verify git tag
|
||||||
|
run: |
|
||||||
|
GIT_TAG=${{ github.ref_name }}
|
||||||
|
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..."
|
||||||
|
else
|
||||||
|
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download artifacts from the "${{ github.ref_name }}" tag
|
||||||
|
uses: robinraju/release-downloader@v1.8
|
||||||
|
with:
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
|
||||||
|
out-file-path: ${{ github.ref_name }}
|
||||||
|
|
||||||
|
- 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 }}
|
||||||
+3
-3
@@ -1,6 +1,3 @@
|
|||||||
[submodule "client/3rd/wireguard-apple"]
|
|
||||||
path = client/3rd/wireguard-apple
|
|
||||||
url = https://github.com/WireGuard/wireguard-apple
|
|
||||||
[submodule "client/3rd/OpenVPNAdapter"]
|
[submodule "client/3rd/OpenVPNAdapter"]
|
||||||
path = client/3rd/OpenVPNAdapter
|
path = client/3rd/OpenVPNAdapter
|
||||||
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
||||||
@@ -25,3 +22,6 @@
|
|||||||
[submodule "client/3rd-prebuilt"]
|
[submodule "client/3rd-prebuilt"]
|
||||||
path = client/3rd-prebuilt
|
path = client/3rd-prebuilt
|
||||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||||
|
[submodule "client/3rd/awg-apple"]
|
||||||
|
path = client/3rd/awg-apple
|
||||||
|
url = https://github.com/amnezia-vpn/awg-apple
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.0.7.1
|
project(${PROJECT} VERSION 4.1.0.1
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
Submodule client/3rd-prebuilt updated: e8795854a5...b4156d4d09
+1
Submodule client/3rd/awg-apple added at 233eda6760
Vendored
-1
Submodule client/3rd/wireguard-apple deleted from 23618f994f
+23
-3
@@ -50,12 +50,30 @@ endif()
|
|||||||
|
|
||||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||||
|
|
||||||
qt6_add_translations(${PROJECT} TS_FILES
|
# -- i18n begin
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
set(AMNEZIAVPN_TS_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
|
||||||
)
|
)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||||
|
|
||||||
|
qt_create_translation(AMNEZIAVPN_QM_FILES ${AMNEZIAVPN_TS_SOURCES} ${AMNEZIAVPN_TS_FILES})
|
||||||
|
|
||||||
|
set(QM_FILE_LIST "")
|
||||||
|
foreach(FILE ${AMNEZIAVPN_QM_FILES})
|
||||||
|
get_filename_component(QM_FILE_NAME ${FILE} NAME)
|
||||||
|
list(APPEND QM_FILE_LIST "<file>${QM_FILE_NAME}</file>")
|
||||||
|
endforeach()
|
||||||
|
string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST})
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||||
|
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||||
|
# -- i18n end
|
||||||
|
|
||||||
if(IOS)
|
if(IOS)
|
||||||
#execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/scripts/run-build-cloak.sh)
|
|
||||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
endif()
|
endif()
|
||||||
@@ -263,6 +281,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
@@ -273,6 +292,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -322,5 +342,5 @@ if(NOT IOS AND NOT ANDROID)
|
|||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
qt_finalize_target(${PROJECT})
|
qt_finalize_target(${PROJECT})
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ void AmneziaApplication::init()
|
|||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
|
|
||||||
initModels();
|
initModels();
|
||||||
|
loadTranslator();
|
||||||
initControllers();
|
initControllers();
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
@@ -138,6 +139,8 @@ void AmneziaApplication::init()
|
|||||||
&ConnectionController::openConnection);
|
&ConnectionController::openConnection);
|
||||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||||
&ConnectionController::closeConnection);
|
&ConnectionController::closeConnection);
|
||||||
|
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
|
||||||
|
&NotificationHandler::onTranslationsUpdated);
|
||||||
|
|
||||||
m_engine->load(url);
|
m_engine->load(url);
|
||||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||||
@@ -221,34 +224,26 @@ void AmneziaApplication::loadTranslator()
|
|||||||
{
|
{
|
||||||
auto locale = m_settings->getAppLanguage();
|
auto locale = m_settings->getAppLanguage();
|
||||||
m_translator.reset(new QTranslator());
|
m_translator.reset(new QTranslator());
|
||||||
if (locale != QLocale::English) {
|
updateTranslator(locale);
|
||||||
if (m_translator->load(locale, QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/i18n"))) {
|
|
||||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
|
||||||
m_settings->setAppLanguage(locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AmneziaApplication::updateTranslator(const QLocale &locale)
|
void AmneziaApplication::updateTranslator(const QLocale &locale)
|
||||||
{
|
{
|
||||||
QResource::registerResource(":/translations.qrc");
|
if (!m_translator->isEmpty()) {
|
||||||
if (!m_translator->isEmpty())
|
|
||||||
QCoreApplication::removeTranslator(m_translator.get());
|
QCoreApplication::removeTranslator(m_translator.get());
|
||||||
|
|
||||||
if (locale == QLocale::English) {
|
|
||||||
m_settings->setAppLanguage(locale);
|
|
||||||
m_engine->retranslate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_translator->load(locale, QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/i18n"))) {
|
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
|
||||||
|
if (m_translator->load(strFileName)) {
|
||||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||||
m_settings->setAppLanguage(locale);
|
m_settings->setAppLanguage(locale);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
m_engine->retranslate();
|
m_settings->setAppLanguage(QLocale::English);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_engine->retranslate();
|
||||||
|
|
||||||
emit translationsUpdated();
|
emit translationsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,11 +279,17 @@ void AmneziaApplication::initModels()
|
|||||||
{
|
{
|
||||||
m_containersModel.reset(new ContainersModel(m_settings, this));
|
m_containersModel.reset(new ContainersModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
||||||
|
connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(),
|
||||||
|
&ContainersModel::updateContainersConfig);
|
||||||
|
|
||||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||||
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
|
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
|
||||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
&ContainersModel::setCurrentlyProcessedServerIndex);
|
||||||
|
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
|
||||||
|
&ContainersModel::setCurrentlyProcessedServerIndex);
|
||||||
|
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
|
||||||
|
&ServersModel::updateContainersConfig);
|
||||||
|
|
||||||
m_languageModel.reset(new LanguageModel(m_settings, this));
|
m_languageModel.reset(new LanguageModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
||||||
@@ -297,14 +298,6 @@ void AmneziaApplication::initModels()
|
|||||||
|
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||||
connect(m_containersModel.get(), &ContainersModel::defaultContainerChanged, this, [this]() {
|
|
||||||
if (m_containersModel->getDefaultContainer() == DockerContainer::WireGuard
|
|
||||||
&& m_sitesModel->getRouteMode() != Settings::RouteMode::VpnAllSites) {
|
|
||||||
m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites);
|
|
||||||
emit m_pageController->showNotificationMessage(
|
|
||||||
tr("Split tunneling for WireGuard is not implemented, the option was disabled"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||||
@@ -318,8 +311,11 @@ void AmneziaApplication::initModels()
|
|||||||
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
||||||
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
||||||
|
|
||||||
m_wireguardConfigModel.reset(new WireGuardConfigModel(this));
|
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
|
||||||
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireguardConfigModel.get());
|
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
|
||||||
|
|
||||||
|
m_awgConfigModel.reset(new AwgConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
||||||
@@ -335,6 +331,9 @@ void AmneziaApplication::initControllers()
|
|||||||
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
||||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||||
|
|
||||||
|
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
|
||||||
|
&ConnectionController::onTranslationsUpdated);
|
||||||
|
|
||||||
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "ui/models/protocols/awgConfigModel.h"
|
||||||
#include "ui/models/protocols/openvpnConfigModel.h"
|
#include "ui/models/protocols/openvpnConfigModel.h"
|
||||||
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
||||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||||
@@ -97,7 +98,8 @@ private:
|
|||||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||||
QScopedPointer<WireGuardConfigModel> m_wireguardConfigModel;
|
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
||||||
|
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
android:label="-- %%INSERT_APP_NAME%% --"
|
android:label="-- %%INSERT_APP_NAME%% --"
|
||||||
android:screenOrientation="unspecified"
|
android:screenOrientation="unspecified"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
||||||
<!-- android:theme="@style/splashScreenTheme"-->
|
<!-- android:theme="@style/splashScreenTheme"-->
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ android {
|
|||||||
resConfig "en"
|
resConfig "en"
|
||||||
minSdkVersion = 24
|
minSdkVersion = 24
|
||||||
targetSdkVersion = 34
|
targetSdkVersion = 34
|
||||||
versionCode 32 // Change to a higher number
|
versionCode 39 // Change to a higher number
|
||||||
versionName "3.0.9" // Change to a higher number
|
versionName "4.1.0" // Change to a higher number
|
||||||
|
|
||||||
javaCompileOptions.annotationProcessorOptions.arguments = [
|
javaCompileOptions.annotationProcessorOptions.arguments = [
|
||||||
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
|
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
|
||||||
|
|||||||
@@ -70,6 +70,15 @@ public class BadConfigException extends Exception {
|
|||||||
EXCLUDED_APPLICATIONS("ExcludedApplications"),
|
EXCLUDED_APPLICATIONS("ExcludedApplications"),
|
||||||
INCLUDED_APPLICATIONS("IncludedApplications"),
|
INCLUDED_APPLICATIONS("IncludedApplications"),
|
||||||
LISTEN_PORT("ListenPort"),
|
LISTEN_PORT("ListenPort"),
|
||||||
|
JC("Jc"),
|
||||||
|
JMIN("Jmin"),
|
||||||
|
JMAX("Jmax"),
|
||||||
|
S1("S1"),
|
||||||
|
S2("S2"),
|
||||||
|
H1("H1"),
|
||||||
|
H2("H2"),
|
||||||
|
H3("H3"),
|
||||||
|
H4("H4"),
|
||||||
MTU("MTU"),
|
MTU("MTU"),
|
||||||
PERSISTENT_KEEPALIVE("PersistentKeepalive"),
|
PERSISTENT_KEEPALIVE("PersistentKeepalive"),
|
||||||
PRE_SHARED_KEY("PresharedKey"),
|
PRE_SHARED_KEY("PresharedKey"),
|
||||||
|
|||||||
@@ -0,0 +1,509 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2017 Tobias Brunner
|
||||||
|
* HSR Hochschule fuer Technik Rapperswil
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.wireguard.config;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a range of IP addresses. This range could be a proper subnet, but that's
|
||||||
|
* not necessarily the case (see {@code getPrefix} and {@code toSubnets}).
|
||||||
|
*/
|
||||||
|
public class IPRange implements Comparable<IPRange>
|
||||||
|
{
|
||||||
|
private final byte[] mBitmask = { (byte)0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
|
||||||
|
private byte[] mFrom;
|
||||||
|
private byte[] mTo;
|
||||||
|
private Integer mPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the range is a proper subnet and, if so, what the network prefix is.
|
||||||
|
*/
|
||||||
|
private void determinePrefix()
|
||||||
|
{
|
||||||
|
boolean matching = true;
|
||||||
|
|
||||||
|
mPrefix = mFrom.length * 8;
|
||||||
|
for (int i = 0; i < mFrom.length; i++)
|
||||||
|
{
|
||||||
|
for (int bit = 0; bit < 8; bit++)
|
||||||
|
{
|
||||||
|
if (matching)
|
||||||
|
{
|
||||||
|
if ((mFrom[i] & mBitmask[bit]) != (mTo[i] & mBitmask[bit]))
|
||||||
|
{
|
||||||
|
mPrefix = (i * 8) + bit;
|
||||||
|
matching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((mFrom[i] & mBitmask[bit]) != 0 || (mTo[i] & mBitmask[bit]) == 0)
|
||||||
|
{
|
||||||
|
mPrefix = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IPRange(byte[] from, byte[] to)
|
||||||
|
{
|
||||||
|
mFrom = from;
|
||||||
|
mTo = to;
|
||||||
|
determinePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPRange(String from, String to) throws UnknownHostException
|
||||||
|
{
|
||||||
|
this(Utils.parseInetAddress(from), Utils.parseInetAddress(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPRange(InetAddress from, InetAddress to)
|
||||||
|
{
|
||||||
|
initializeFromRange(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeFromRange(InetAddress from, InetAddress to)
|
||||||
|
{
|
||||||
|
byte[] fa = from.getAddress(), ta = to.getAddress();
|
||||||
|
if (fa.length != ta.length)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invalid range");
|
||||||
|
}
|
||||||
|
if (compareAddr(fa, ta) < 0)
|
||||||
|
{
|
||||||
|
mFrom = fa;
|
||||||
|
mTo = ta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTo = fa;
|
||||||
|
mFrom = ta;
|
||||||
|
}
|
||||||
|
determinePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPRange(String base, int prefix) throws UnknownHostException
|
||||||
|
{
|
||||||
|
this(Utils.parseInetAddress(base), prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPRange(InetAddress base, int prefix)
|
||||||
|
{
|
||||||
|
this(base.getAddress(), prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IPRange(byte[] from, int prefix)
|
||||||
|
{
|
||||||
|
initializeFromCIDR(from, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeFromCIDR(byte[] from, int prefix)
|
||||||
|
{
|
||||||
|
if (from.length != 4 && from.length != 16)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invalid address");
|
||||||
|
}
|
||||||
|
if (prefix < 0 || prefix > from.length * 8)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invalid prefix");
|
||||||
|
}
|
||||||
|
byte[] to = from.clone();
|
||||||
|
byte mask = (byte)(0xff << (8 - prefix % 8));
|
||||||
|
int i = prefix / 8;
|
||||||
|
|
||||||
|
if (i < from.length)
|
||||||
|
{
|
||||||
|
from[i] = (byte)(from[i] & mask);
|
||||||
|
to[i] = (byte)(to[i] | ~mask);
|
||||||
|
Arrays.fill(from, i+1, from.length, (byte)0);
|
||||||
|
Arrays.fill(to, i+1, to.length, (byte)0xff);
|
||||||
|
}
|
||||||
|
mFrom = from;
|
||||||
|
mTo = to;
|
||||||
|
mPrefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPRange(String cidr) throws UnknownHostException
|
||||||
|
{
|
||||||
|
/* only verify the basic structure */
|
||||||
|
if (!cidr.matches("(?i)^(([0-9.]+)|([0-9a-f:]+))(-(([0-9.]+)|([0-9a-f:]+))|(/\\d+))?$"))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invalid CIDR or range notation");
|
||||||
|
}
|
||||||
|
if (cidr.contains("-"))
|
||||||
|
{
|
||||||
|
String[] parts = cidr.split("-");
|
||||||
|
InetAddress from = InetAddress.getByName(parts[0]);
|
||||||
|
InetAddress to = InetAddress.getByName(parts[1]);
|
||||||
|
initializeFromRange(from, to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String[] parts = cidr.split("/");
|
||||||
|
InetAddress addr = InetAddress.getByName(parts[0]);
|
||||||
|
byte[] base = addr.getAddress();
|
||||||
|
int prefix = base.length * 8;
|
||||||
|
if (parts.length > 1)
|
||||||
|
{
|
||||||
|
prefix = Integer.parseInt(parts[1]);
|
||||||
|
}
|
||||||
|
initializeFromCIDR(base, prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first address of the range. The network ID in case this is a proper subnet.
|
||||||
|
*/
|
||||||
|
public InetAddress getFrom()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return InetAddress.getByAddress(mFrom);
|
||||||
|
}
|
||||||
|
catch (UnknownHostException ignored)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last address of the range.
|
||||||
|
*/
|
||||||
|
public InetAddress getTo()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return InetAddress.getByAddress(mTo);
|
||||||
|
}
|
||||||
|
catch (UnknownHostException ignored)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this range is a proper subnet returns its prefix, otherwise returns null.
|
||||||
|
*/
|
||||||
|
public Integer getPrefix()
|
||||||
|
{
|
||||||
|
return mPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NonNull IPRange other)
|
||||||
|
{
|
||||||
|
int cmp = compareAddr(mFrom, other.mFrom);
|
||||||
|
if (cmp == 0)
|
||||||
|
{ /* smaller ranges first */
|
||||||
|
cmp = compareAddr(mTo, other.mTo);
|
||||||
|
}
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (o == null || !(o instanceof IPRange))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this == o || compareTo((IPRange)o) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (mPrefix != null)
|
||||||
|
{
|
||||||
|
return InetAddress.getByAddress(mFrom).getHostAddress() + "/" + mPrefix;
|
||||||
|
}
|
||||||
|
return InetAddress.getByAddress(mFrom).getHostAddress() + "-" +
|
||||||
|
InetAddress.getByAddress(mTo).getHostAddress();
|
||||||
|
}
|
||||||
|
catch (UnknownHostException ignored)
|
||||||
|
{
|
||||||
|
return super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareAddr(byte a[], byte b[])
|
||||||
|
{
|
||||||
|
if (a.length != b.length)
|
||||||
|
{
|
||||||
|
return (a.length < b.length) ? -1 : 1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < a.length; i++)
|
||||||
|
{
|
||||||
|
if (a[i] != b[i])
|
||||||
|
{
|
||||||
|
if (((int)a[i] & 0xff) < ((int)b[i] & 0xff))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this range fully contains the given range.
|
||||||
|
*/
|
||||||
|
public boolean contains(IPRange range)
|
||||||
|
{
|
||||||
|
return compareAddr(mFrom, range.mFrom) <= 0 && compareAddr(range.mTo, mTo) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this and the given range overlap.
|
||||||
|
*/
|
||||||
|
public boolean overlaps(IPRange range)
|
||||||
|
{
|
||||||
|
return !(compareAddr(mTo, range.mFrom) < 0 || compareAddr(range.mTo, mFrom) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] dec(byte[] addr)
|
||||||
|
{
|
||||||
|
for (int i = addr.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (--addr[i] != (byte)0xff)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] inc(byte[] addr)
|
||||||
|
{
|
||||||
|
for (int i = addr.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (++addr[i] != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given range from the current range. Returns a list of resulting ranges (these are
|
||||||
|
* not proper subnets). At most two ranges are returned, in case the given range is contained in
|
||||||
|
* this but does not equal it, which would result in an empty list (which is also the case if
|
||||||
|
* this range is fully contained in the given range).
|
||||||
|
*/
|
||||||
|
public List<IPRange> remove(IPRange range)
|
||||||
|
{
|
||||||
|
ArrayList<IPRange> list = new ArrayList<>();
|
||||||
|
if (!overlaps(range))
|
||||||
|
{ /* | this | or | this |
|
||||||
|
* | range | | range | */
|
||||||
|
list.add(this);
|
||||||
|
}
|
||||||
|
else if (!range.contains(this))
|
||||||
|
{ /* we are not completely removed, so none of these cases applies:
|
||||||
|
* | this | or | this | or | this |
|
||||||
|
* | range | | range | | range | */
|
||||||
|
if (compareAddr(mFrom, range.mFrom) < 0 && compareAddr(range.mTo, mTo) < 0)
|
||||||
|
{ /* the removed range is completely within our boundaries:
|
||||||
|
* | this |
|
||||||
|
* | range | */
|
||||||
|
list.add(new IPRange(mFrom, dec(range.mFrom.clone())));
|
||||||
|
list.add(new IPRange(inc(range.mTo.clone()), mTo));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* one end is within our boundaries the other at or outside it:
|
||||||
|
* | this | or | this | or | this | or | this |
|
||||||
|
* | range | | range | | range | | range | */
|
||||||
|
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : inc(range.mTo.clone());
|
||||||
|
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : dec(range.mFrom.clone());
|
||||||
|
list.add(new IPRange(from, to));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean adjacent(IPRange range)
|
||||||
|
{
|
||||||
|
if (compareAddr(mTo, range.mFrom) < 0)
|
||||||
|
{
|
||||||
|
byte[] to = inc(mTo.clone());
|
||||||
|
return compareAddr(to, range.mFrom) == 0;
|
||||||
|
}
|
||||||
|
byte[] from = dec(mFrom.clone());
|
||||||
|
return compareAddr(from, range.mTo) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge two adjacent or overlapping ranges, returns null if it's not possible to merge them.
|
||||||
|
*/
|
||||||
|
public IPRange merge(IPRange range)
|
||||||
|
{
|
||||||
|
if (overlaps(range))
|
||||||
|
{
|
||||||
|
if (contains(range))
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
else if (range.contains(this))
|
||||||
|
{
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!adjacent(range))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : range.mFrom;
|
||||||
|
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : range.mTo;
|
||||||
|
return new IPRange(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the given range into a sorted list of proper subnets.
|
||||||
|
*/
|
||||||
|
public List<IPRange> toSubnets()
|
||||||
|
{
|
||||||
|
ArrayList<IPRange> list = new ArrayList<>();
|
||||||
|
if (mPrefix != null)
|
||||||
|
{
|
||||||
|
list.add(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = 0, bit = 0, prefix, netmask, common_byte, common_bit;
|
||||||
|
int from_cur, from_prev = 0, to_cur, to_prev = 1;
|
||||||
|
boolean from_full = true, to_full = true;
|
||||||
|
|
||||||
|
byte[] from = mFrom.clone();
|
||||||
|
byte[] to = mTo.clone();
|
||||||
|
|
||||||
|
/* find a common prefix */
|
||||||
|
while (i < from.length && (from[i] & mBitmask[bit]) == (to[i] & mBitmask[bit]))
|
||||||
|
{
|
||||||
|
if (++bit == 8)
|
||||||
|
{
|
||||||
|
bit = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefix = i * 8 + bit;
|
||||||
|
|
||||||
|
/* at this point we know that the addresses are either equal, or that the
|
||||||
|
* current bits in the 'from' and 'to' addresses are 0 and 1, respectively.
|
||||||
|
* we now look at the rest of the bits as two binary trees (0=left, 1=right)
|
||||||
|
* where 'from' and 'to' are both leaf nodes. all leaf nodes between these
|
||||||
|
* nodes are addresses contained in the range. to collect them as subnets
|
||||||
|
* we follow the trees from both leaf nodes to their root node and record
|
||||||
|
* all complete subtrees (right for from, left for to) we come across as
|
||||||
|
* subnets. in that process host bits are zeroed out. if both addresses
|
||||||
|
* are equal we won't enter the loop below.
|
||||||
|
* 0_____|_____1 for the 'from' address we assume we start on a
|
||||||
|
* 0__|__ 1 0__|__1 left subtree (0) and follow the left edges until
|
||||||
|
* _|_ _|_ _|_ _|_ we reach the root of this subtree, which is
|
||||||
|
* | | | | | | | | either the root of this whole 'from'-subtree
|
||||||
|
* 0 1 0 1 0 1 0 1 (causing us to leave the loop) or the root node
|
||||||
|
* of the right subtree (1) of another node (which actually could be the
|
||||||
|
* leaf node we start from). that whole subtree gets recorded as subnet.
|
||||||
|
* next we follow the right edges to the root of that subtree which again is
|
||||||
|
* either the 'from'-root or the root node in the left subtree (0) of
|
||||||
|
* another node. the complete right subtree of that node is the next subnet
|
||||||
|
* we record. from there we assume that we are in that right subtree and
|
||||||
|
* recursively follow right edges to its root. for the 'to' address the
|
||||||
|
* procedure is exactly the same but with left and right reversed.
|
||||||
|
*/
|
||||||
|
if (++bit == 8)
|
||||||
|
{
|
||||||
|
bit = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
common_byte = i;
|
||||||
|
common_bit = bit;
|
||||||
|
netmask = from.length * 8;
|
||||||
|
for (i = from.length - 1; i >= common_byte; i--)
|
||||||
|
{
|
||||||
|
int bit_min = (i == common_byte) ? common_bit : 0;
|
||||||
|
for (bit = 7; bit >= bit_min; bit--)
|
||||||
|
{
|
||||||
|
byte mask = mBitmask[bit];
|
||||||
|
|
||||||
|
from_cur = from[i] & mask;
|
||||||
|
if (from_prev == 0 && from_cur != 0)
|
||||||
|
{ /* 0 -> 1: subnet is the whole current (right) subtree */
|
||||||
|
list.add(new IPRange(from.clone(), netmask));
|
||||||
|
from_full = false;
|
||||||
|
}
|
||||||
|
else if (from_prev != 0 && from_cur == 0)
|
||||||
|
{ /* 1 -> 0: invert bit to switch to right subtree and add it */
|
||||||
|
from[i] ^= mask;
|
||||||
|
list.add(new IPRange(from.clone(), netmask));
|
||||||
|
from_cur = 1;
|
||||||
|
}
|
||||||
|
/* clear the current bit */
|
||||||
|
from[i] &= ~mask;
|
||||||
|
from_prev = from_cur;
|
||||||
|
|
||||||
|
to_cur = to[i] & mask;
|
||||||
|
if (to_prev != 0 && to_cur == 0)
|
||||||
|
{ /* 1 -> 0: subnet is the whole current (left) subtree */
|
||||||
|
list.add(new IPRange(to.clone(), netmask));
|
||||||
|
to_full = false;
|
||||||
|
}
|
||||||
|
else if (to_prev == 0 && to_cur != 0)
|
||||||
|
{ /* 0 -> 1: invert bit to switch to left subtree and add it */
|
||||||
|
to[i] ^= mask;
|
||||||
|
list.add(new IPRange(to.clone(), netmask));
|
||||||
|
to_cur = 0;
|
||||||
|
}
|
||||||
|
/* clear the current bit */
|
||||||
|
to[i] &= ~mask;
|
||||||
|
to_prev = to_cur;
|
||||||
|
netmask--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from_full && to_full)
|
||||||
|
{ /* full subnet (from=to or from=0.. and to=1.. after common prefix) - not reachable
|
||||||
|
* due to the shortcut at the top */
|
||||||
|
list.add(new IPRange(from.clone(), prefix));
|
||||||
|
}
|
||||||
|
else if (from_full)
|
||||||
|
{ /* full from subnet (from=0.. after prefix) */
|
||||||
|
list.add(new IPRange(from.clone(), prefix + 1));
|
||||||
|
}
|
||||||
|
else if (to_full)
|
||||||
|
{ /* full to subnet (to=1.. after prefix) */
|
||||||
|
list.add(new IPRange(to.clone(), prefix + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2017 Tobias Brunner
|
||||||
|
* HSR Hochschule fuer Technik Rapperswil
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.wireguard.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
|
||||||
|
* modifying the set and enumerating the resulting subnets.
|
||||||
|
*/
|
||||||
|
public class IPRangeSet implements Iterable<IPRange>
|
||||||
|
{
|
||||||
|
private TreeSet<IPRange> mRanges = new TreeSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given string (space separated ranges in CIDR or range notation) and return the
|
||||||
|
* resulting set or {@code null} if the string was invalid. An empty set is returned if the given string
|
||||||
|
* is {@code null}.
|
||||||
|
*/
|
||||||
|
public static IPRangeSet fromString(String ranges)
|
||||||
|
{
|
||||||
|
IPRangeSet set = new IPRangeSet();
|
||||||
|
if (ranges != null)
|
||||||
|
{
|
||||||
|
for (String range : ranges.split("\\s+"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
set.add(new IPRange(range));
|
||||||
|
}
|
||||||
|
catch (Exception unused)
|
||||||
|
{ /* besides due to invalid strings exceptions might get thrown if the string
|
||||||
|
* contains a hostname (NetworkOnMainThreadException) */
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a range to this set. Automatically gets merged with existing ranges.
|
||||||
|
*/
|
||||||
|
public void add(IPRange range)
|
||||||
|
{
|
||||||
|
if (mRanges.contains(range))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinsert:
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Iterator<IPRange> iterator = mRanges.iterator();
|
||||||
|
while (iterator.hasNext())
|
||||||
|
{
|
||||||
|
IPRange existing = iterator.next();
|
||||||
|
IPRange replacement = existing.merge(range);
|
||||||
|
if (replacement != null)
|
||||||
|
{
|
||||||
|
iterator.remove();
|
||||||
|
range = replacement;
|
||||||
|
continue reinsert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mRanges.add(range);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all ranges from the given set.
|
||||||
|
*/
|
||||||
|
public void add(IPRangeSet ranges)
|
||||||
|
{
|
||||||
|
if (ranges == this)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (IPRange range : ranges.mRanges)
|
||||||
|
{
|
||||||
|
add(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all ranges from the given collection to this set.
|
||||||
|
*/
|
||||||
|
public void addAll(Collection<? extends IPRange> coll)
|
||||||
|
{
|
||||||
|
for (IPRange range : coll)
|
||||||
|
{
|
||||||
|
add(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given range from this set. Existing ranges are automatically adjusted.
|
||||||
|
*/
|
||||||
|
public void remove(IPRange range)
|
||||||
|
{
|
||||||
|
ArrayList <IPRange> additions = new ArrayList<>();
|
||||||
|
Iterator<IPRange> iterator = mRanges.iterator();
|
||||||
|
while (iterator.hasNext())
|
||||||
|
{
|
||||||
|
IPRange existing = iterator.next();
|
||||||
|
List<IPRange> result = existing.remove(range);
|
||||||
|
if (result.size() == 0)
|
||||||
|
{
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
else if (!result.get(0).equals(existing))
|
||||||
|
{
|
||||||
|
iterator.remove();
|
||||||
|
additions.addAll(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mRanges.addAll(additions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given ranges from ranges in this set.
|
||||||
|
*/
|
||||||
|
public void remove(IPRangeSet ranges)
|
||||||
|
{
|
||||||
|
if (ranges == this)
|
||||||
|
{
|
||||||
|
mRanges.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (IPRange range : ranges.mRanges)
|
||||||
|
{
|
||||||
|
remove(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the subnets derived from all the ranges in this set.
|
||||||
|
*/
|
||||||
|
public Iterable<IPRange> subnets()
|
||||||
|
{
|
||||||
|
return new Iterable<IPRange>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Iterator<IPRange> iterator()
|
||||||
|
{
|
||||||
|
return new Iterator<IPRange>()
|
||||||
|
{
|
||||||
|
private Iterator<IPRange> mIterator = mRanges.iterator();
|
||||||
|
private List<IPRange> mSubnets;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext()
|
||||||
|
{
|
||||||
|
return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPRange next()
|
||||||
|
{
|
||||||
|
if (mSubnets == null || mSubnets.size() == 0)
|
||||||
|
{
|
||||||
|
IPRange range = mIterator.next();
|
||||||
|
mSubnets = range.toSubnets();
|
||||||
|
}
|
||||||
|
return mSubnets.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<IPRange> iterator()
|
||||||
|
{
|
||||||
|
return mRanges.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of ranges, not subnets.
|
||||||
|
*/
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return mRanges.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{ /* we could use TextUtils, but that causes the unit tests to fail */
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (IPRange range : mRanges)
|
||||||
|
{
|
||||||
|
if (sb.length() > 0)
|
||||||
|
{
|
||||||
|
sb.append(" ");
|
||||||
|
}
|
||||||
|
sb.append(range.toString());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,15 @@ public final class Interface {
|
|||||||
private final KeyPair keyPair;
|
private final KeyPair keyPair;
|
||||||
private final Optional<Integer> listenPort;
|
private final Optional<Integer> listenPort;
|
||||||
private final Optional<Integer> mtu;
|
private final Optional<Integer> mtu;
|
||||||
|
private final Optional<Integer> jc;
|
||||||
|
private final Optional<Integer> jmin;
|
||||||
|
private final Optional<Integer> jmax;
|
||||||
|
private final Optional<Integer> s1;
|
||||||
|
private final Optional<Integer> s2;
|
||||||
|
private final Optional<Long> h1;
|
||||||
|
private final Optional<Long> h2;
|
||||||
|
private final Optional<Long> h3;
|
||||||
|
private final Optional<Long> h4;
|
||||||
|
|
||||||
private Interface(final Builder builder) {
|
private Interface(final Builder builder) {
|
||||||
// Defensively copy to ensure immutability even if the Builder is reused.
|
// Defensively copy to ensure immutability even if the Builder is reused.
|
||||||
@@ -56,6 +65,15 @@ public final class Interface {
|
|||||||
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
|
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
|
||||||
listenPort = builder.listenPort;
|
listenPort = builder.listenPort;
|
||||||
mtu = builder.mtu;
|
mtu = builder.mtu;
|
||||||
|
jc = builder.jc;
|
||||||
|
jmax = builder.jmax;
|
||||||
|
jmin = builder.jmin;
|
||||||
|
s1 = builder.s1;
|
||||||
|
s2 = builder.s2;
|
||||||
|
h1 = builder.h1;
|
||||||
|
h2 = builder.h2;
|
||||||
|
h3 = builder.h3;
|
||||||
|
h4 = builder.h4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,6 +113,33 @@ public final class Interface {
|
|||||||
case "privatekey":
|
case "privatekey":
|
||||||
builder.parsePrivateKey(attribute.getValue());
|
builder.parsePrivateKey(attribute.getValue());
|
||||||
break;
|
break;
|
||||||
|
case "jc":
|
||||||
|
builder.parseJc(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "jmin":
|
||||||
|
builder.parseJmin(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "jmax":
|
||||||
|
builder.parseJmax(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "s1":
|
||||||
|
builder.parseS1(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "s2":
|
||||||
|
builder.parseS2(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "h1":
|
||||||
|
builder.parseH1(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "h2":
|
||||||
|
builder.parseH2(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "h3":
|
||||||
|
builder.parseH3(attribute.getValue());
|
||||||
|
break;
|
||||||
|
case "h4":
|
||||||
|
builder.parseH4(attribute.getValue());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new BadConfigException(
|
throw new BadConfigException(
|
||||||
Section.INTERFACE, Location.TOP_LEVEL, Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
|
Section.INTERFACE, Location.TOP_LEVEL, Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
|
||||||
@@ -111,7 +156,9 @@ public final class Interface {
|
|||||||
return addresses.equals(other.addresses) && dnsServers.equals(other.dnsServers)
|
return addresses.equals(other.addresses) && dnsServers.equals(other.dnsServers)
|
||||||
&& excludedApplications.equals(other.excludedApplications)
|
&& excludedApplications.equals(other.excludedApplications)
|
||||||
&& includedApplications.equals(other.includedApplications) && keyPair.equals(other.keyPair)
|
&& includedApplications.equals(other.includedApplications) && keyPair.equals(other.keyPair)
|
||||||
&& listenPort.equals(other.listenPort) && mtu.equals(other.mtu);
|
&& listenPort.equals(other.listenPort) && mtu.equals(other.mtu) && jc.equals(other.jc) && jmin.equals(other.jmin)
|
||||||
|
&& jmax.equals(other.jmax) && s1.equals(other.s1) && s2.equals(other.s2) && h1.equals(other.h1) && h2.equals(other.h2)
|
||||||
|
&& h3.equals(other.h3) && h4.equals(other.h4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,6 +228,42 @@ public final class Interface {
|
|||||||
return mtu;
|
return mtu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getJc() {
|
||||||
|
return jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getJmin() {
|
||||||
|
return jmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getJmax() {
|
||||||
|
return jmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getS1() {
|
||||||
|
return s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getS2() {
|
||||||
|
return s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getH1() {
|
||||||
|
return h1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getH2() {
|
||||||
|
return h2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getH3() {
|
||||||
|
return h3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getH4() {
|
||||||
|
return h4;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 1;
|
int hash = 1;
|
||||||
@@ -191,6 +274,15 @@ public final class Interface {
|
|||||||
hash = 31 * hash + keyPair.hashCode();
|
hash = 31 * hash + keyPair.hashCode();
|
||||||
hash = 31 * hash + listenPort.hashCode();
|
hash = 31 * hash + listenPort.hashCode();
|
||||||
hash = 31 * hash + mtu.hashCode();
|
hash = 31 * hash + mtu.hashCode();
|
||||||
|
hash = 31 * hash + jc.hashCode();
|
||||||
|
hash = 31 * hash + jmin.hashCode();
|
||||||
|
hash = 31 * hash + jmax.hashCode();
|
||||||
|
hash = 31 * hash + s1.hashCode();
|
||||||
|
hash = 31 * hash + s2.hashCode();
|
||||||
|
hash = 31 * hash + h1.hashCode();
|
||||||
|
hash = 31 * hash + h2.hashCode();
|
||||||
|
hash = 31 * hash + h3.hashCode();
|
||||||
|
hash = 31 * hash + h4.hashCode();
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +326,19 @@ public final class Interface {
|
|||||||
.append('\n');
|
.append('\n');
|
||||||
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
|
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
|
||||||
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
|
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
|
||||||
|
|
||||||
|
jc.ifPresent(t_jc -> sb.append("Jc = ").append(t_jc).append('\n'));
|
||||||
|
jmin.ifPresent(t_jmin -> sb.append("Jmin = ").append(t_jmin).append('\n'));
|
||||||
|
jmax.ifPresent(t_jmax -> sb.append("Jmax = ").append(t_jmax).append('\n'));
|
||||||
|
|
||||||
|
s1.ifPresent(t_s1 -> sb.append("S1 = ").append(t_s1).append('\n'));
|
||||||
|
s2.ifPresent(t_s2 -> sb.append("S2 = ").append(t_s2).append('\n'));
|
||||||
|
|
||||||
|
h1.ifPresent(t_h1 -> sb.append("H1 = ").append(t_h1).append('\n'));
|
||||||
|
h2.ifPresent(t_h2 -> sb.append("H2 = ").append(t_h2).append('\n'));
|
||||||
|
h3.ifPresent(t_h3 -> sb.append("H3 = ").append(t_h3).append('\n'));
|
||||||
|
h4.ifPresent(t_h4 -> sb.append("H4 = ").append(t_h4).append('\n'));
|
||||||
|
|
||||||
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
|
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -248,6 +353,18 @@ public final class Interface {
|
|||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append("private_key=").append(keyPair.getPrivateKey().toHex()).append('\n');
|
sb.append("private_key=").append(keyPair.getPrivateKey().toHex()).append('\n');
|
||||||
listenPort.ifPresent(lp -> sb.append("listen_port=").append(lp).append('\n'));
|
listenPort.ifPresent(lp -> sb.append("listen_port=").append(lp).append('\n'));
|
||||||
|
|
||||||
|
jc.ifPresent(t_jc -> sb.append("jc=").append(t_jc).append('\n'));
|
||||||
|
jmin.ifPresent(t_jmin -> sb.append("jmin=").append(t_jmin).append('\n'));
|
||||||
|
jmax.ifPresent(t_jmax -> sb.append("jmax=").append(t_jmax).append('\n'));
|
||||||
|
|
||||||
|
s1.ifPresent(t_s1 -> sb.append("s1=").append(t_s1).append('\n'));
|
||||||
|
s2.ifPresent(t_s2 -> sb.append("s2=").append(t_s2).append('\n'));
|
||||||
|
|
||||||
|
h1.ifPresent(t_h1 -> sb.append("h1=").append(t_h1).append('\n'));
|
||||||
|
h2.ifPresent(t_h2 -> sb.append("h2=").append(t_h2).append('\n'));
|
||||||
|
h3.ifPresent(t_h3 -> sb.append("h3=").append(t_h3).append('\n'));
|
||||||
|
h4.ifPresent(t_h4 -> sb.append("h4=").append(t_h4).append('\n'));
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +384,17 @@ public final class Interface {
|
|||||||
private Optional<Integer> listenPort = Optional.empty();
|
private Optional<Integer> listenPort = Optional.empty();
|
||||||
// Defaults to not present.
|
// Defaults to not present.
|
||||||
private Optional<Integer> mtu = Optional.empty();
|
private Optional<Integer> mtu = Optional.empty();
|
||||||
|
private Optional<Integer> jc = Optional.empty();
|
||||||
|
private Optional<Integer> jmin = Optional.empty();
|
||||||
|
private Optional<Integer> jmax = Optional.empty();
|
||||||
|
|
||||||
|
private Optional<Integer> s1 = Optional.empty();
|
||||||
|
private Optional<Integer> s2 = Optional.empty();
|
||||||
|
|
||||||
|
private Optional<Long> h1 = Optional.empty();
|
||||||
|
private Optional<Long> h2 = Optional.empty();
|
||||||
|
private Optional<Long> h3 = Optional.empty();
|
||||||
|
private Optional<Long> h4 = Optional.empty();
|
||||||
|
|
||||||
public Builder addAddress(final InetNetwork address) {
|
public Builder addAddress(final InetNetwork address) {
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
@@ -362,6 +490,78 @@ public final class Interface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder parseJc(final String jc) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setJc(Integer.parseInt(jc));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.JC, jc, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseJmax(final String jmax) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setJmax(Integer.parseInt(jmax));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.JMAX, jmax, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseJmin(final String jmin) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setJmin(Integer.parseInt(jmin));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.JMIN, jmin, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseS1(final String s1) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setS1(Integer.parseInt(s1));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.S1, s1, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseS2(final String s2) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setS2(Integer.parseInt(s2));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.S2, s2, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseH1(final String h1) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setH1(Long.parseLong(h1));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.H1, h1, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseH2(final String h2) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setH2(Long.parseLong(h2));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.H2, h2, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseH3(final String h3) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setH3(Long.parseLong(h3));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.H3, h3, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parseH4(final String h4) throws BadConfigException {
|
||||||
|
try {
|
||||||
|
return setH4(Long.parseLong(h4));
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new BadConfigException(Section.INTERFACE, Location.H4, h4, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
|
public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
|
||||||
try {
|
try {
|
||||||
return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
|
return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
|
||||||
@@ -386,9 +586,81 @@ public final class Interface {
|
|||||||
public Builder setMtu(final int mtu) throws BadConfigException {
|
public Builder setMtu(final int mtu) throws BadConfigException {
|
||||||
if (mtu < 0)
|
if (mtu < 0)
|
||||||
throw new BadConfigException(
|
throw new BadConfigException(
|
||||||
Section.INTERFACE, Location.LISTEN_PORT, Reason.INVALID_VALUE, String.valueOf(mtu));
|
Section.INTERFACE, Location.MTU, Reason.INVALID_VALUE, String.valueOf(mtu));
|
||||||
this.mtu = mtu == 0 ? Optional.empty() : Optional.of(mtu);
|
this.mtu = mtu == 0 ? Optional.empty() : Optional.of(mtu);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setJc(final int jc) throws BadConfigException {
|
||||||
|
if (jc < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.JC, Reason.INVALID_VALUE, String.valueOf(jc));
|
||||||
|
this.jc = Optional.of(jc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setJmin(final int jmin) throws BadConfigException {
|
||||||
|
if (jmin < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.JMIN, Reason.INVALID_VALUE, String.valueOf(jmin));
|
||||||
|
this.jmin = Optional.of(jmin);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setJmax(final int jmax) throws BadConfigException {
|
||||||
|
if (jmax < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.JMAX, Reason.INVALID_VALUE, String.valueOf(jmax));
|
||||||
|
this.jmax = Optional.of(jmax);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setS1(final int s1) throws BadConfigException {
|
||||||
|
if (s1 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.S1, Reason.INVALID_VALUE, String.valueOf(s1));
|
||||||
|
this.s1 = Optional.of(s1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setS2(final int s2) throws BadConfigException {
|
||||||
|
if (s2 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.S2, Reason.INVALID_VALUE, String.valueOf(s2));
|
||||||
|
this.s2 = Optional.of(s2);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setH1(final long h1) throws BadConfigException {
|
||||||
|
if (h1 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.H1, Reason.INVALID_VALUE, String.valueOf(h1));
|
||||||
|
this.h1 = Optional.of(h1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setH2(final long h2) throws BadConfigException {
|
||||||
|
if (h2 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.H2, Reason.INVALID_VALUE, String.valueOf(h2));
|
||||||
|
this.h2 = Optional.of(h2);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setH3(final long h3) throws BadConfigException {
|
||||||
|
if (h3 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.H3, Reason.INVALID_VALUE, String.valueOf(h3));
|
||||||
|
this.h3 = Optional.of(h3);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setH4(final long h4) throws BadConfigException {
|
||||||
|
if (h4 < 0)
|
||||||
|
throw new BadConfigException(
|
||||||
|
Section.INTERFACE, Location.H4, Reason.INVALID_VALUE, String.valueOf(h4));
|
||||||
|
this.h4 = Optional.of(h4);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014-2019 Tobias Brunner
|
||||||
|
* HSR Hochschule fuer Technik Rapperswil
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.wireguard.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
public class Utils
|
||||||
|
{
|
||||||
|
static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given byte array to a hexadecimal string encoding.
|
||||||
|
*
|
||||||
|
* @param bytes byte array to convert
|
||||||
|
* @return hex string
|
||||||
|
*/
|
||||||
|
public static String bytesToHex(byte[] bytes)
|
||||||
|
{
|
||||||
|
char[] hex = new char[bytes.length * 2];
|
||||||
|
for (int i = 0; i < bytes.length; i++)
|
||||||
|
{
|
||||||
|
int value = bytes[i];
|
||||||
|
hex[i*2] = HEXDIGITS[(value & 0xf0) >> 4];
|
||||||
|
hex[i*2+1] = HEXDIGITS[ value & 0x0f];
|
||||||
|
}
|
||||||
|
return new String(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the given proposal string
|
||||||
|
*
|
||||||
|
* @param ike true for IKE, false for ESP
|
||||||
|
* @param proposal proposal string
|
||||||
|
* @return true if valid
|
||||||
|
*/
|
||||||
|
public native static boolean isProposalValid(boolean ike, String proposal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an IP address without doing a name lookup
|
||||||
|
*
|
||||||
|
* @param address IP address string
|
||||||
|
* @return address bytes if valid
|
||||||
|
*/
|
||||||
|
private native static byte[] parseInetAddressBytes(String address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an IP address without doing a name lookup (as compared to InetAddress.fromName())
|
||||||
|
*
|
||||||
|
* @param address IP address string
|
||||||
|
* @return address if valid
|
||||||
|
* @throws UnknownHostException if address is invalid
|
||||||
|
*/
|
||||||
|
public static InetAddress parseInetAddress(String address) throws UnknownHostException
|
||||||
|
{
|
||||||
|
byte[] bytes = parseInetAddressBytes(address);
|
||||||
|
if (bytes == null)
|
||||||
|
{
|
||||||
|
throw new UnknownHostException();
|
||||||
|
}
|
||||||
|
return InetAddress.getByAddress(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ import com.wireguard.crypto.Key
|
|||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
|
|
||||||
|
import com.wireguard.config.*
|
||||||
|
|
||||||
import net.openvpn.ovpn3.ClientAPI_Config
|
import net.openvpn.ovpn3.ClientAPI_Config
|
||||||
import net.openvpn.ovpn3.ClientAPI_EvalConfig
|
import net.openvpn.ovpn3.ClientAPI_EvalConfig
|
||||||
import net.openvpn.ovpn3.ClientAPI_Event
|
import net.openvpn.ovpn3.ClientAPI_Event
|
||||||
@@ -72,6 +74,8 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
|
|||||||
|
|
||||||
val jsonVpnConfig = mService.getVpnConfig()
|
val jsonVpnConfig = mService.getVpnConfig()
|
||||||
val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config")
|
val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config")
|
||||||
|
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
|
||||||
|
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
|
||||||
|
|
||||||
val resultingConfig = StringBuilder()
|
val resultingConfig = StringBuilder()
|
||||||
resultingConfig.append(ovpnConfig)
|
resultingConfig.append(ovpnConfig)
|
||||||
@@ -115,6 +119,7 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
|
|||||||
eval_config(config)
|
eval_config(config)
|
||||||
|
|
||||||
val status = connect()
|
val status = connect()
|
||||||
|
|
||||||
if (status.getError()) {
|
if (status.getError()) {
|
||||||
Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage())
|
Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage())
|
||||||
}
|
}
|
||||||
@@ -139,6 +144,31 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
|
|||||||
|
|
||||||
override fun tun_builder_establish(): Int {
|
override fun tun_builder_establish(): Int {
|
||||||
Log.v(tag, "tun_builder_establish")
|
Log.v(tag, "tun_builder_establish")
|
||||||
|
val jsonVpnConfig = mService.getVpnConfig()
|
||||||
|
|
||||||
|
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
|
||||||
|
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
|
||||||
|
if (splitTunnelType == 1) {
|
||||||
|
for (i in 0 until splitTunnelSites.length()) {
|
||||||
|
val site = splitTunnelSites.getString(i)
|
||||||
|
val ipRange = IPRange(site)
|
||||||
|
mService.addRoute(ipRange.getFrom().getHostAddress(), ipRange.getPrefix())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (splitTunnelType == 2) {
|
||||||
|
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
|
||||||
|
ipRangeSet.remove(IPRange("127.0.0.0/8"))
|
||||||
|
for (i in 0 until splitTunnelSites.length()) {
|
||||||
|
val site = splitTunnelSites.getString(i)
|
||||||
|
ipRangeSet.remove(IPRange(site))
|
||||||
|
}
|
||||||
|
ipRangeSet.subnets().forEach {
|
||||||
|
mService.addRoute(it.getFrom().getHostAddress(), it.getPrefix())
|
||||||
|
Thread.sleep(10)
|
||||||
|
}
|
||||||
|
mService.addRoute("2000::", 3)
|
||||||
|
}
|
||||||
|
|
||||||
return mService.establish()!!.detachFd()
|
return mService.establish()!!.detachFd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -380,7 +380,10 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||||||
mNetworkState.bindNetworkListener()
|
mNetworkState.bindNetworkListener()
|
||||||
}
|
}
|
||||||
"wireguard" -> {
|
"wireguard" -> {
|
||||||
startWireGuard()
|
startWireGuard("wireguard")
|
||||||
|
}
|
||||||
|
"awg" -> {
|
||||||
|
startWireGuard("awg")
|
||||||
}
|
}
|
||||||
"shadowsocks" -> {
|
"shadowsocks" -> {
|
||||||
startShadowsocks()
|
startShadowsocks()
|
||||||
@@ -457,7 +460,8 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||||||
fun turnOff() {
|
fun turnOff() {
|
||||||
Log.v(tag, "Aman: turnOff....................")
|
Log.v(tag, "Aman: turnOff....................")
|
||||||
when (mProtocol) {
|
when (mProtocol) {
|
||||||
"wireguard" -> {
|
"wireguard",
|
||||||
|
"awg" -> {
|
||||||
GoBackend.wgTurnOff(currentTunnelHandle)
|
GoBackend.wgTurnOff(currentTunnelHandle)
|
||||||
}
|
}
|
||||||
"cloak",
|
"cloak",
|
||||||
@@ -560,36 +564,75 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||||||
return parseData
|
return parseData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Wireguard [Config] from a [json] string -
|
* Create a Wireguard [Config] from a [json] string -
|
||||||
* The [json] will be created in AndroidVpnProtocol.cpp
|
* The [json] will be created in AndroidVpnProtocol.cpp
|
||||||
*/
|
*/
|
||||||
private fun buildWireguardConfig(obj: JSONObject): Config {
|
private fun buildWireguardConfig(obj: JSONObject, type: String): Config {
|
||||||
val confBuilder = Config.Builder()
|
val confBuilder = Config.Builder()
|
||||||
val wireguardConfigData = obj.getJSONObject("wireguard_config_data")
|
val wireguardConfigData = obj.getJSONObject(type)
|
||||||
|
val splitTunnelType = obj.getInt("splitTunnelType")
|
||||||
|
val splitTunnelSites = obj.getJSONArray("splitTunnelSites")
|
||||||
|
|
||||||
val config = parseConfigData(wireguardConfigData.getString("config"))
|
val config = parseConfigData(wireguardConfigData.getString("config"))
|
||||||
val peerBuilder = Peer.Builder()
|
val peerBuilder = Peer.Builder()
|
||||||
val peerConfig = config["Peer"]!!
|
val peerConfig = config["Peer"]!!
|
||||||
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
|
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
|
||||||
peerConfig["PresharedKey"]?.let {
|
peerConfig["PresharedKey"]?.let { peerBuilder.setPreSharedKey(Key.fromBase64(it)) }
|
||||||
peerBuilder.setPreSharedKey(Key.fromBase64(it))
|
|
||||||
|
val allIpString = peerConfig["AllowedIPs"]
|
||||||
|
|
||||||
|
var allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
|
||||||
|
|
||||||
|
/* default value in template */
|
||||||
|
if (allIpString == "0.0.0.0/0, ::/0") {
|
||||||
|
allowedIPList = emptyList()
|
||||||
}
|
}
|
||||||
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
|
|
||||||
if (allowedIPList.isEmpty()) {
|
if (allowedIPList.isEmpty() && (splitTunnelType == 0)) {
|
||||||
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
|
/* AllowedIP is empty and splitTunnel is turnoff */
|
||||||
peerBuilder.addAllowedIp(internet)
|
/* use VPN for whole Internet */
|
||||||
|
val internetV4 = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
|
||||||
|
peerBuilder.addAllowedIp(internetV4)
|
||||||
|
val internetV6 = InetNetwork.parse("::/0") // aka The whole internet.
|
||||||
|
peerBuilder.addAllowedIp(internetV6)
|
||||||
} else {
|
} else {
|
||||||
allowedIPList.forEach {
|
if (!allowedIPList.isEmpty()) {
|
||||||
val network = InetNetwork.parse(it.trim())
|
/* We have predefined AllowedIP in WG config */
|
||||||
peerBuilder.addAllowedIp(network)
|
/* It's have higher priority than system SplitTunnel */
|
||||||
|
allowedIPList.forEach {
|
||||||
|
val network = InetNetwork.parse(it.trim())
|
||||||
|
peerBuilder.addAllowedIp(network)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (splitTunnelType == 1) {
|
||||||
|
/* Use system SplitTunnel */
|
||||||
|
/* VPN connection used only for defined IPs */
|
||||||
|
for (i in 0 until splitTunnelSites.length()) {
|
||||||
|
val site = splitTunnelSites.getString(i)
|
||||||
|
val internet = InetNetwork.parse(site)
|
||||||
|
peerBuilder.addAllowedIp(internet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (splitTunnelType == 2) {
|
||||||
|
/* Use system SplitTunnel */
|
||||||
|
/* VPN connection used for all Internet exclude defined IPs */
|
||||||
|
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
|
||||||
|
ipRangeSet.remove(IPRange("127.0.0.0/8"))
|
||||||
|
for (i in 0 until splitTunnelSites.length()) {
|
||||||
|
val site = splitTunnelSites.getString(i)
|
||||||
|
ipRangeSet.remove(IPRange(site))
|
||||||
|
}
|
||||||
|
val allowedIps = ipRangeSet.subnets().joinToString(", ") + ", 2000::/3"
|
||||||
|
peerBuilder.parseAllowedIPs(allowedIps)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val endpointConfig = peerConfig["Endpoint"]
|
val endpointConfig = peerConfig["Endpoint"]
|
||||||
val endpoint = InetEndpoint.parse(endpointConfig)
|
val endpoint = InetEndpoint.parse(endpointConfig)
|
||||||
peerBuilder.setEndpoint(endpoint)
|
peerBuilder.setEndpoint(endpoint)
|
||||||
peerConfig["PersistentKeepalive"]?.let {
|
peerConfig["PersistentKeepalive"]?.let { peerBuilder.setPersistentKeepalive(it.toInt()) }
|
||||||
peerBuilder.setPersistentKeepalive(it.toInt())
|
|
||||||
}
|
|
||||||
confBuilder.addPeer(peerBuilder.build())
|
confBuilder.addPeer(peerBuilder.build())
|
||||||
|
|
||||||
val ifaceBuilder = Interface.Builder()
|
val ifaceBuilder = Interface.Builder()
|
||||||
@@ -599,11 +642,34 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||||||
ifaceConfig["DNS"]!!.split(",").forEach {
|
ifaceConfig["DNS"]!!.split(",").forEach {
|
||||||
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
|
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
|
||||||
|
if (type == "awg_config_data") {
|
||||||
|
ifaceBuilder.parseJc(ifaceConfig["Jc"])
|
||||||
|
ifaceBuilder.parseJmin(ifaceConfig["Jmin"])
|
||||||
|
ifaceBuilder.parseJmax(ifaceConfig["Jmax"])
|
||||||
|
ifaceBuilder.parseS1(ifaceConfig["S1"])
|
||||||
|
ifaceBuilder.parseS2(ifaceConfig["S2"])
|
||||||
|
ifaceBuilder.parseH1(ifaceConfig["H1"])
|
||||||
|
ifaceBuilder.parseH2(ifaceConfig["H2"])
|
||||||
|
ifaceBuilder.parseH3(ifaceConfig["H3"])
|
||||||
|
ifaceBuilder.parseH4(ifaceConfig["H4"])
|
||||||
|
} else {
|
||||||
|
ifaceBuilder.parseJc("0")
|
||||||
|
ifaceBuilder.parseJmin("0")
|
||||||
|
ifaceBuilder.parseJmax("0")
|
||||||
|
ifaceBuilder.parseS1("0")
|
||||||
|
ifaceBuilder.parseS2("0")
|
||||||
|
ifaceBuilder.parseH1("0")
|
||||||
|
ifaceBuilder.parseH2("0")
|
||||||
|
ifaceBuilder.parseH3("0")
|
||||||
|
ifaceBuilder.parseH4("0")
|
||||||
|
}
|
||||||
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
|
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
|
||||||
(0 until jExcludedApplication.length()).toList().forEach {
|
(0 until jExcludedApplication.length()).toList().forEach {
|
||||||
val appName = jExcludedApplication.get(it).toString()
|
val appName = jExcludedApplication.get(it).toString()
|
||||||
ifaceBuilder.excludeApplication(appName)
|
ifaceBuilder.excludeApplication(appName)
|
||||||
}*/
|
}*/
|
||||||
confBuilder.setInterface(ifaceBuilder.build())
|
confBuilder.setInterface(ifaceBuilder.build())
|
||||||
|
|
||||||
return confBuilder.build()
|
return confBuilder.build()
|
||||||
@@ -716,21 +782,27 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startWireGuard() {
|
private fun startWireGuard(type: String) {
|
||||||
val wireguard_conf = buildWireguardConfig(mConfig!!)
|
val wireguard_conf = buildWireguardConfig(mConfig!!, type + "_config_data")
|
||||||
Log.i(tag, "startWireGuard: wireguard_conf : $wireguard_conf")
|
|
||||||
if (currentTunnelHandle != -1) {
|
if (currentTunnelHandle != -1) {
|
||||||
Log.e(tag, "Tunnel already up")
|
Log.e(tag, "Tunnel already up")
|
||||||
// Turn the tunnel down because this might be a switch
|
// Turn the tunnel down because this might be a switch
|
||||||
GoBackend.wgTurnOff(currentTunnelHandle)
|
GoBackend.wgTurnOff(currentTunnelHandle)
|
||||||
}
|
}
|
||||||
val wgConfig: String = wireguard_conf.toWgUserspaceString()
|
val wgConfig: String = wireguard_conf.toWgUserspaceString()
|
||||||
|
|
||||||
val builder = Builder()
|
val builder = Builder()
|
||||||
setupBuilder(wireguard_conf, builder)
|
setupBuilder(wireguard_conf, builder)
|
||||||
builder.setSession("Amnezia")
|
builder.setSession("Amnezia")
|
||||||
|
|
||||||
|
|
||||||
builder.establish().use { tun ->
|
builder.establish().use { tun ->
|
||||||
if (tun == null) return
|
if (tun == null) return
|
||||||
currentTunnelHandle = GoBackend.wgTurnOn("Amnezia", tun.detachFd(), wgConfig)
|
if (type == "awg"){
|
||||||
|
currentTunnelHandle = GoBackend.wgTurnOn("awg0", tun.detachFd(), wgConfig)
|
||||||
|
} else {
|
||||||
|
currentTunnelHandle = GoBackend.wgTurnOn("amn0", tun.detachFd(), wgConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (currentTunnelHandle < 0) {
|
if (currentTunnelHandle < 0) {
|
||||||
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
|
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ target_compile_options(${PROJECT} PRIVATE
|
|||||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources)
|
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/awg-apple/Sources)
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE
|
target_sources(${PROJECT} PRIVATE
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#include "awg_configurator.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "core/servercontroller.h"
|
||||||
|
|
||||||
|
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
|
: WireguardConfigurator(settings, true, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials,
|
||||||
|
DockerContainer container,
|
||||||
|
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||||
|
{
|
||||||
|
QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
|
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||||
|
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
||||||
|
|
||||||
|
QMap<QString, QString> configMap;
|
||||||
|
auto configLines = awgConfig.split("\n");
|
||||||
|
for (auto &line : configLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
|
||||||
|
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
|
||||||
|
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
|
||||||
|
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
|
||||||
|
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
|
||||||
|
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
|
||||||
|
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);
|
||||||
|
|
||||||
|
return QJsonDocument(jsonConfig).toJson();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef AWGCONFIGURATOR_H
|
||||||
|
#define AWGCONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "wireguard_configurator.h"
|
||||||
|
|
||||||
|
class AwgConfigurator : public WireguardConfigurator
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AWGCONFIGURATOR_H
|
||||||
@@ -131,10 +131,13 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
|
|||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
}
|
}
|
||||||
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
|
#endif
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|||||||
@@ -1,32 +1,34 @@
|
|||||||
#include "vpn_configurator.h"
|
#include "vpn_configurator.h"
|
||||||
#include "openvpn_configurator.h"
|
|
||||||
#include "cloak_configurator.h"
|
#include "cloak_configurator.h"
|
||||||
#include "shadowsocks_configurator.h"
|
|
||||||
#include "wireguard_configurator.h"
|
|
||||||
#include "ikev2_configurator.h"
|
#include "ikev2_configurator.h"
|
||||||
|
#include "openvpn_configurator.h"
|
||||||
|
#include "shadowsocks_configurator.h"
|
||||||
#include "ssh_configurator.h"
|
#include "ssh_configurator.h"
|
||||||
|
#include "wireguard_configurator.h"
|
||||||
|
#include "awg_configurator.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "utilities.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
|
||||||
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
ConfiguratorBase(settings, parent)
|
: ConfiguratorBase(settings, parent)
|
||||||
{
|
{
|
||||||
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, this));
|
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, this));
|
||||||
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, this));
|
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, this));
|
||||||
cloakConfigurator = std::shared_ptr<CloakConfigurator>(new CloakConfigurator(settings, this));
|
cloakConfigurator = std::shared_ptr<CloakConfigurator>(new CloakConfigurator(settings, this));
|
||||||
wireguardConfigurator = std::shared_ptr<WireguardConfigurator>(new WireguardConfigurator(settings, this));
|
wireguardConfigurator = std::shared_ptr<WireguardConfigurator>(new WireguardConfigurator(settings, false, this));
|
||||||
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, this));
|
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, this));
|
||||||
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
|
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
|
||||||
|
awgConfigurator = std::shared_ptr<AwgConfigurator>(new AwgConfigurator(settings, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials,
|
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
DockerContainer container, const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode)
|
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
switch (proto) {
|
switch (proto) {
|
||||||
case Proto::OpenVpn:
|
case Proto::OpenVpn:
|
||||||
@@ -35,17 +37,17 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia
|
|||||||
case Proto::ShadowSocks:
|
case Proto::ShadowSocks:
|
||||||
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
|
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
case Proto::Cloak:
|
case Proto::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
|
||||||
return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
|
|
||||||
|
|
||||||
case Proto::WireGuard:
|
case Proto::WireGuard:
|
||||||
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode);
|
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
case Proto::Ikev2:
|
case Proto::Awg:
|
||||||
return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
|
return awgConfigurator->genAwgConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
default:
|
case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
|
||||||
return "";
|
|
||||||
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,8 +64,8 @@ QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
|
|||||||
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
|
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
|
||||||
if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) {
|
if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) {
|
||||||
dns.first = protocols::dns::amneziaDnsIp;
|
dns.first = protocols::dns::amneziaDnsIp;
|
||||||
}
|
} else
|
||||||
else dns.first = m_settings->primaryDns();
|
dns.first = m_settings->primaryDns();
|
||||||
}
|
}
|
||||||
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
|
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
|
||||||
dns.second = m_settings->secondaryDns();
|
dns.second = m_settings->secondaryDns();
|
||||||
@@ -73,8 +75,8 @@ QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
|
|||||||
return dns;
|
return dns;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container,
|
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||||
Proto proto, QString &config)
|
QString &config)
|
||||||
{
|
{
|
||||||
auto dns = getDnsForConfig(serverIndex);
|
auto dns = getDnsForConfig(serverIndex);
|
||||||
|
|
||||||
@@ -84,8 +86,8 @@ QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerCo
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container,
|
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||||
Proto proto, QString &config)
|
QString &config)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||||
|
|
||||||
@@ -95,8 +97,8 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container,
|
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto,
|
||||||
Proto proto, QString &config)
|
QString &config)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, Docke
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VpnConfigurator::updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
|
void VpnConfigurator::updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
|
||||||
const QString &stdOut)
|
const QString &stdOut)
|
||||||
{
|
{
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ class CloakConfigurator;
|
|||||||
class WireguardConfigurator;
|
class WireguardConfigurator;
|
||||||
class Ikev2Configurator;
|
class Ikev2Configurator;
|
||||||
class SshConfigurator;
|
class SshConfigurator;
|
||||||
|
class AwgConfigurator;
|
||||||
|
|
||||||
// Retrieve connection settings from server
|
// Retrieve connection settings from server
|
||||||
class VpnConfigurator : ConfiguratorBase
|
class VpnConfigurator : ConfiguratorBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
explicit VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
|
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
|
||||||
@@ -40,6 +41,7 @@ public:
|
|||||||
std::shared_ptr<WireguardConfigurator> wireguardConfigurator;
|
std::shared_ptr<WireguardConfigurator> wireguardConfigurator;
|
||||||
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
|
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
|
||||||
std::shared_ptr<SshConfigurator> sshConfigurator;
|
std::shared_ptr<SshConfigurator> sshConfigurator;
|
||||||
|
std::shared_ptr<AwgConfigurator> awgConfigurator;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPN_CONFIGURATOR_H
|
#endif // VPN_CONFIGURATOR_H
|
||||||
|
|||||||
@@ -19,9 +19,20 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent)
|
||||||
: ConfiguratorBase(settings, parent)
|
: ConfiguratorBase(settings, 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_configTemplate = m_isAwg ? ProtocolScriptType::awg_template
|
||||||
|
: ProtocolScriptType::wireguard_template;
|
||||||
|
|
||||||
|
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||||
|
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||||
@@ -62,7 +73,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
{
|
{
|
||||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
connData.port = containerConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
|
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
|
||||||
|
|
||||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
@@ -76,7 +87,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
// Get list of already created clients (only IP addresses)
|
// Get list of already created clients (only IP addresses)
|
||||||
QString nextIpNumber;
|
QString nextIpNumber;
|
||||||
{
|
{
|
||||||
QString script = QString("cat %1 | grep AllowedIPs").arg(amnezia::protocols::wireguard::serverConfigPath);
|
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
@@ -123,8 +134,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get keys
|
// Get keys
|
||||||
connData.serverPubKey = serverController.getTextFileFromContainer(
|
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, &e);
|
||||||
container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
|
|
||||||
connData.serverPubKey.replace("\n", "");
|
connData.serverPubKey.replace("\n", "");
|
||||||
if (e) {
|
if (e) {
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
@@ -132,8 +142,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
connData.pskKey = serverController.getTextFileFromContainer(container, credentials,
|
connData.pskKey = serverController.getTextFileFromContainer(container, credentials, m_serverPskKeyPath, &e);
|
||||||
amnezia::protocols::wireguard::serverPskKeyPath, &e);
|
|
||||||
connData.pskKey.replace("\n", "");
|
connData.pskKey.replace("\n", "");
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
@@ -147,12 +156,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
"PublicKey = %1\n"
|
"PublicKey = %1\n"
|
||||||
"PresharedKey = %2\n"
|
"PresharedKey = %2\n"
|
||||||
"AllowedIPs = %3/32\n\n")
|
"AllowedIPs = %3/32\n\n")
|
||||||
.arg(connData.clientPubKey)
|
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
|
||||||
.arg(connData.pskKey)
|
|
||||||
.arg(connData.clientIP);
|
|
||||||
|
|
||||||
e = serverController.uploadTextFileToContainer(container, credentials, configPart,
|
e = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath,
|
||||||
protocols::wireguard::serverConfigPath,
|
|
||||||
libssh::SftpOverwriteMode::SftpAppendToExisting);
|
libssh::SftpOverwriteMode::SftpAppendToExisting);
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
@@ -161,11 +167,11 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||||
|
.arg(m_serverConfigPath);
|
||||||
|
|
||||||
e = serverController.runScript(
|
e = serverController.runScript(
|
||||||
credentials,
|
credentials, serverController.replaceVars(script, serverController.genVarsForScript(credentials, container)));
|
||||||
serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick "
|
|
||||||
"strip /opt/amnezia/wireguard/wg0.conf)'",
|
|
||||||
serverController.genVarsForScript(credentials, container)));
|
|
||||||
|
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
@@ -174,9 +180,9 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
|
|||||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
QString config =
|
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||||
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
|
QString config = serverController.replaceVars(
|
||||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
scriptData, serverController.genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||||
if (errorCode && *errorCode) {
|
if (errorCode && *errorCode) {
|
||||||
|
|||||||
@@ -6,35 +6,44 @@
|
|||||||
|
|
||||||
#include "configurator_base.h"
|
#include "configurator_base.h"
|
||||||
#include "core/defs.h"
|
#include "core/defs.h"
|
||||||
|
#include "core/scripts_registry.h"
|
||||||
|
|
||||||
class WireguardConfigurator : ConfiguratorBase
|
class WireguardConfigurator : public ConfiguratorBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
WireguardConfigurator(std::shared_ptr<Settings> settings, bool isAwg, QObject *parent = nullptr);
|
||||||
|
|
||||||
struct ConnectionData {
|
struct ConnectionData
|
||||||
|
{
|
||||||
QString clientPrivKey; // client private key
|
QString clientPrivKey; // client private key
|
||||||
QString clientPubKey; // client public key
|
QString clientPubKey; // client public key
|
||||||
QString clientIP; // internal client IP address
|
QString clientIP; // internal client IP address
|
||||||
QString serverPubKey; // tls-auth key
|
QString serverPubKey; // tls-auth key
|
||||||
QString pskKey; // preshared key
|
QString pskKey; // preshared key
|
||||||
QString host; // host ip
|
QString host; // host ip
|
||||||
QString port;
|
QString port;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(QString config);
|
QString processConfigWithLocalSettings(QString config);
|
||||||
QString processConfigWithExportSettings(QString config);
|
QString processConfigWithExportSettings(QString config);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials,
|
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
ConnectionData genClientKeys();
|
ConnectionData genClientKeys();
|
||||||
|
|
||||||
|
bool m_isAwg;
|
||||||
|
QString m_serverConfigPath;
|
||||||
|
QString m_serverPublicKeyPath;
|
||||||
|
QString m_serverPskKeyPath;
|
||||||
|
amnezia::ProtocolScriptType m_configTemplate;
|
||||||
|
QString m_protocolName;
|
||||||
|
QString m_defaultPort;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARD_CONFIGURATOR_H
|
#endif // WIREGUARD_CONFIGURATOR_H
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
|
|||||||
{ DockerContainer::ShadowSocks, "ShadowSocks" },
|
{ DockerContainer::ShadowSocks, "ShadowSocks" },
|
||||||
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
|
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
|
||||||
{ DockerContainer::WireGuard, "WireGuard" },
|
{ DockerContainer::WireGuard, "WireGuard" },
|
||||||
|
{ DockerContainer::Awg, "AmneziaWG" },
|
||||||
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
|
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
|
||||||
|
|
||||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||||
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
|
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
|
||||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service")},
|
|
||||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
|
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +107,10 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
|||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
||||||
"consumption. Recommended for regions with low levels of censorship.") },
|
"consumption. Recommended for regions with low levels of censorship.") },
|
||||||
|
{ DockerContainer::Awg,
|
||||||
|
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
|
||||||
|
"but very resistant to blockages. "
|
||||||
|
"Recommended for regions with high levels of censorship.") },
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
|
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
|
||||||
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
||||||
@@ -114,42 +118,108 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
|||||||
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
|
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
|
||||||
{ DockerContainer::Dns,
|
{ DockerContainer::Dns,
|
||||||
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
|
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
|
||||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")},
|
|
||||||
{ DockerContainer::Sftp,
|
{ DockerContainer::Sftp,
|
||||||
QObject::tr("Creates a file vault on your server to securely store and transfer files.") } };
|
QObject::tr("Creates a file vault on your server to securely store and transfer files.") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||||
{
|
{
|
||||||
return { { DockerContainer::OpenVpn, QObject::tr("OpenVPN container") },
|
return {
|
||||||
{ DockerContainer::ShadowSocks, QObject::tr("Container with OpenVpn and ShadowSocks") },
|
{ DockerContainer::OpenVpn,
|
||||||
{ DockerContainer::Cloak,
|
QObject::tr(
|
||||||
QObject::tr("Container with OpenVpn and ShadowSocks protocols "
|
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
||||||
"configured with traffic masking by Cloak plugin") },
|
"It employs its unique security protocol, "
|
||||||
{ DockerContainer::WireGuard, QObject::tr("WireGuard container") },
|
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
||||||
{ DockerContainer::Ipsec, QObject::tr("IPsec container") },
|
"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 analysis systems and therefore susceptible to blocking\n"
|
||||||
|
"* Can operate over both TCP and UDP network 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"
|
||||||
|
"* Normal power consumption on mobile devices\n\n"
|
||||||
|
"* Configurable encryption protocol\n"
|
||||||
|
"* Detectable by some DPI systems\n"
|
||||||
|
"* Works over TCP network protocol.") },
|
||||||
|
{ DockerContainer::Cloak,
|
||||||
|
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
||||||
|
"blocking protection.\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 and blocking. \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"
|
||||||
|
"If there is a extreme level of Internet censorship in your region, we advise you to use only "
|
||||||
|
"OpenVPN over Cloak from the first connection\n\n"
|
||||||
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
|
"* High power consumption on mobile devices\n"
|
||||||
|
"* Flexible settings\n"
|
||||||
|
"* Not recognised by DPI analysis systems\n"
|
||||||
|
"* Works over TCP network protocol, 443 port.\n") },
|
||||||
|
{ DockerContainer::WireGuard,
|
||||||
|
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
||||||
|
"Provides stable VPN connection, high performance on all devices. Uses hard-coded encryption "
|
||||||
|
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
||||||
|
"WireGuard is very susceptible to 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.") },
|
||||||
|
{ 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 DPI analysis systems, resistant to blocking\n"
|
||||||
|
"* Works over UDP network 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.") },
|
||||||
|
|
||||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")},
|
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service") }
|
||||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service") } };
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
amnezia::ServiceType ContainerProps::containerService(DockerContainer c)
|
amnezia::ServiceType ContainerProps::containerService(DockerContainer c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
return ProtocolProps::protocolService(defaultProtocol(c));
|
||||||
case DockerContainer::None: return ServiceType::None;
|
|
||||||
case DockerContainer::OpenVpn: return ServiceType::Vpn;
|
|
||||||
case DockerContainer::Cloak: return ServiceType::Vpn;
|
|
||||||
case DockerContainer::ShadowSocks: return ServiceType::Vpn;
|
|
||||||
case DockerContainer::WireGuard: return ServiceType::Vpn;
|
|
||||||
case DockerContainer::Ipsec: return ServiceType::Vpn;
|
|
||||||
case DockerContainer::TorWebSite: return ServiceType::Other;
|
|
||||||
case DockerContainer::Dns: return ServiceType::Other;
|
|
||||||
// case DockerContainer::FileShare : return ServiceType::Other;
|
|
||||||
case DockerContainer::Sftp: return ServiceType::Other;
|
|
||||||
default: return ServiceType::Other;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Proto ContainerProps::defaultProtocol(DockerContainer c)
|
Proto ContainerProps::defaultProtocol(DockerContainer c)
|
||||||
@@ -160,11 +230,11 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
|
|||||||
case DockerContainer::Cloak: return Proto::Cloak;
|
case DockerContainer::Cloak: return Proto::Cloak;
|
||||||
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
|
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
|
||||||
case DockerContainer::WireGuard: return Proto::WireGuard;
|
case DockerContainer::WireGuard: return Proto::WireGuard;
|
||||||
|
case DockerContainer::Awg: return Proto::Awg;
|
||||||
case DockerContainer::Ipsec: return Proto::Ikev2;
|
case DockerContainer::Ipsec: return Proto::Ikev2;
|
||||||
|
|
||||||
case DockerContainer::TorWebSite: return Proto::TorWebSite;
|
case DockerContainer::TorWebSite: return Proto::TorWebSite;
|
||||||
case DockerContainer::Dns: return Proto::Dns;
|
case DockerContainer::Dns: return Proto::Dns;
|
||||||
// case DockerContainer::FileShare : return Protocol::FileShare;
|
|
||||||
case DockerContainer::Sftp: return Proto::Sftp;
|
case DockerContainer::Sftp: return Proto::Sftp;
|
||||||
default: return Proto::Any;
|
default: return Proto::Any;
|
||||||
}
|
}
|
||||||
@@ -179,6 +249,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
switch (c) {
|
switch (c) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
|
case DockerContainer::Awg: return true;
|
||||||
case DockerContainer::Cloak:
|
case DockerContainer::Cloak:
|
||||||
return true;
|
return true;
|
||||||
// case DockerContainer::ShadowSocks: return true;
|
// case DockerContainer::ShadowSocks: return true;
|
||||||
@@ -196,6 +267,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
case DockerContainer::ShadowSocks: return true;
|
case DockerContainer::ShadowSocks: return true;
|
||||||
|
case DockerContainer::Awg: return true;
|
||||||
case DockerContainer::Cloak: return true;
|
case DockerContainer::Cloak: return true;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
@@ -224,8 +296,8 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
|||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
|
case DockerContainer::Awg: return true;
|
||||||
case DockerContainer::Cloak: return true;
|
case DockerContainer::Cloak: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,8 +306,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
|||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return tr("Low");
|
case DockerContainer::WireGuard: return tr("Low");
|
||||||
case DockerContainer::Cloak: return tr("High");
|
case DockerContainer::Awg: return tr("Medium or High");
|
||||||
case DockerContainer::OpenVpn: return tr("Medium");
|
case DockerContainer::Cloak: return tr("Extreme");
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,9 +315,10 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
|||||||
QString ContainerProps::easySetupDescription(DockerContainer container)
|
QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return tr("I just want to increase the level of privacy");
|
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
|
||||||
case DockerContainer::Cloak: return tr("Many foreign websites and VPN providers are blocked");
|
case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
|
||||||
case DockerContainer::OpenVpn: return tr("Some foreign sites are blocked, but VPN providers are not blocked");
|
case DockerContainer::Cloak:
|
||||||
|
return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,9 +326,9 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
|||||||
int ContainerProps::easySetupOrder(DockerContainer container)
|
int ContainerProps::easySetupOrder(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return 1;
|
case DockerContainer::WireGuard: return 3;
|
||||||
case DockerContainer::Cloak: return 3;
|
case DockerContainer::Awg: return 2;
|
||||||
case DockerContainer::OpenVpn: return 2;
|
case DockerContainer::Cloak: return 1;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,16 +16,16 @@ namespace amnezia
|
|||||||
Q_NAMESPACE
|
Q_NAMESPACE
|
||||||
enum DockerContainer {
|
enum DockerContainer {
|
||||||
None = 0,
|
None = 0,
|
||||||
OpenVpn,
|
Awg,
|
||||||
ShadowSocks,
|
|
||||||
Cloak,
|
|
||||||
WireGuard,
|
WireGuard,
|
||||||
|
OpenVpn,
|
||||||
|
Cloak,
|
||||||
|
ShadowSocks,
|
||||||
Ipsec,
|
Ipsec,
|
||||||
|
|
||||||
// non-vpn
|
// non-vpn
|
||||||
TorWebSite,
|
TorWebSite,
|
||||||
Dns,
|
Dns,
|
||||||
// FileShare,
|
|
||||||
Sftp
|
Sftp
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(DockerContainer)
|
Q_ENUM_NS(DockerContainer)
|
||||||
|
|||||||
+1
-3
@@ -36,7 +36,7 @@ enum ErrorCode
|
|||||||
ServerPacketManagerError,
|
ServerPacketManagerError,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequsetDeniedError, SshInterruptedError, SshInternalError,
|
SshRequestDeniedError, SshInterruptedError, SshInternalError,
|
||||||
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
|
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
|
||||||
|
|
||||||
// Ssh sftp errors
|
// Ssh sftp errors
|
||||||
@@ -47,7 +47,6 @@ enum ErrorCode
|
|||||||
SshSftpNoMediaError,
|
SshSftpNoMediaError,
|
||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
FailedToSaveConfigData,
|
|
||||||
OpenVpnConfigMissing,
|
OpenVpnConfigMissing,
|
||||||
OpenVpnManagementServerError,
|
OpenVpnManagementServerError,
|
||||||
ConfigMissing,
|
ConfigMissing,
|
||||||
@@ -67,7 +66,6 @@ enum ErrorCode
|
|||||||
|
|
||||||
// 3rd party utils errors
|
// 3rd party utils errors
|
||||||
OpenSslFailed,
|
OpenSslFailed,
|
||||||
OpenVpnExecutableCrashed,
|
|
||||||
ShadowSocksExecutableCrashed,
|
ShadowSocksExecutableCrashed,
|
||||||
CloakExecutableCrashed,
|
CloakExecutableCrashed,
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ QString errorString(ErrorCode code){
|
|||||||
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
|
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
|
||||||
|
|
||||||
// Libssh errors
|
// Libssh errors
|
||||||
case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied");
|
case(SshRequestDeniedError): return QObject::tr("Ssh request was denied");
|
||||||
case(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
|
case(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
|
||||||
case(SshInternalError): return QObject::tr("Ssh internal error");
|
case(SshInternalError): return QObject::tr("Ssh internal error");
|
||||||
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
|
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
|
||||||
@@ -42,7 +42,6 @@ QString errorString(ErrorCode code){
|
|||||||
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
|
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
|
||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk");
|
|
||||||
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
|
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
|
||||||
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");
|
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "scripts_registry.h"
|
#include "scripts_registry.h"
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
||||||
{
|
{
|
||||||
@@ -11,11 +11,11 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
|||||||
case DockerContainer::Cloak: return QLatin1String("openvpn_cloak");
|
case DockerContainer::Cloak: return QLatin1String("openvpn_cloak");
|
||||||
case DockerContainer::ShadowSocks: return QLatin1String("openvpn_shadowsocks");
|
case DockerContainer::ShadowSocks: return QLatin1String("openvpn_shadowsocks");
|
||||||
case DockerContainer::WireGuard: return QLatin1String("wireguard");
|
case DockerContainer::WireGuard: return QLatin1String("wireguard");
|
||||||
|
case DockerContainer::Awg: return QLatin1String("awg");
|
||||||
case DockerContainer::Ipsec: return QLatin1String("ipsec");
|
case DockerContainer::Ipsec: return QLatin1String("ipsec");
|
||||||
|
|
||||||
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
|
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
|
||||||
case DockerContainer::Dns: return QLatin1String("dns");
|
case DockerContainer::Dns: return QLatin1String("dns");
|
||||||
//case DockerContainer::FileShare: return QLatin1String("file_share");
|
|
||||||
case DockerContainer::Sftp: return QLatin1String("sftp");
|
case DockerContainer::Sftp: return QLatin1String("sftp");
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,7 @@ QString amnezia::scriptName(ProtocolScriptType type)
|
|||||||
case ProtocolScriptType::container_startup: return QLatin1String("start.sh");
|
case ProtocolScriptType::container_startup: return QLatin1String("start.sh");
|
||||||
case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn");
|
case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn");
|
||||||
case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf");
|
case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf");
|
||||||
|
case ProtocolScriptType::awg_template: return QLatin1String("template.conf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ QString amnezia::scriptData(amnezia::SharedScriptType type)
|
|||||||
{
|
{
|
||||||
QString fileName = QString(":/server_scripts/%1").arg(amnezia::scriptName(type));
|
QString fileName = QString(":/server_scripts/%1").arg(amnezia::scriptName(type));
|
||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
if (! file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
qDebug() << "Warning: script missing" << fileName;
|
qDebug() << "Warning: script missing" << fileName;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ QString amnezia::scriptData(amnezia::ProtocolScriptType type, DockerContainer co
|
|||||||
{
|
{
|
||||||
QString fileName = QString(":/server_scripts/%1/%2").arg(amnezia::scriptFolder(container), amnezia::scriptName(type));
|
QString fileName = QString(":/server_scripts/%1/%2").arg(amnezia::scriptFolder(container), amnezia::scriptName(type));
|
||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
if (! file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
qDebug() << "Warning: script missing" << fileName;
|
qDebug() << "Warning: script missing" << fileName;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ enum ProtocolScriptType {
|
|||||||
configure_container,
|
configure_container,
|
||||||
container_startup,
|
container_startup,
|
||||||
openvpn_template,
|
openvpn_template,
|
||||||
wireguard_template
|
wireguard_template,
|
||||||
|
awg_template
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -168,10 +168,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
runScript(credentials,
|
runScript(credentials,
|
||||||
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||||
|
|
||||||
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,6 +335,10 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (container == DockerContainer::Awg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +487,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||||||
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
||||||
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
||||||
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||||
|
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
|
||||||
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
||||||
|
|
||||||
Vars vars;
|
Vars vars;
|
||||||
@@ -582,6 +584,25 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||||||
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
|
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
|
||||||
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||||
|
|
||||||
|
// Amnezia wireguard vars
|
||||||
|
vars.append({ { "$AWG_SERVER_PORT",
|
||||||
|
amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
||||||
|
|
||||||
|
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
|
||||||
|
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
|
||||||
|
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
|
||||||
|
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
|
||||||
|
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE",
|
||||||
|
amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
|
||||||
|
vars.append({ { "$INIT_PACKET_MAGIC_HEADER",
|
||||||
|
amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
|
||||||
|
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER",
|
||||||
|
amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
|
||||||
|
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER",
|
||||||
|
amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||||
|
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER",
|
||||||
|
amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||||
|
|
||||||
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
||||||
if (!serverIp.isEmpty()) {
|
if (!serverIp.isEmpty()) {
|
||||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||||
@@ -810,6 +831,34 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
|||||||
containerConfig.insert(config_key::port, port);
|
containerConfig.insert(config_key::port, port);
|
||||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||||
|
|
||||||
|
if (protocol == Proto::Awg) {
|
||||||
|
QString serverConfig = getTextFileFromContainer(container, credentials, protocols::awg::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::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
|
||||||
|
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
|
||||||
|
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
|
||||||
|
containerConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
|
||||||
|
containerConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
|
||||||
|
containerConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
|
||||||
|
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
|
||||||
|
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||||
|
containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
|
||||||
|
}
|
||||||
|
|
||||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
}
|
}
|
||||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ namespace libssh {
|
|||||||
|
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
case(SSH_NO_ERROR): return ErrorCode::NoError;
|
case(SSH_NO_ERROR): return ErrorCode::NoError;
|
||||||
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError;
|
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequestDeniedError;
|
||||||
case(SSH_EINTR): return ErrorCode::SshInterruptedError;
|
case(SSH_EINTR): return ErrorCode::SshInterruptedError;
|
||||||
case(SSH_FATAL): return ErrorCode::SshInternalError;
|
case(SSH_FATAL): return ErrorCode::SshInternalError;
|
||||||
default: return ErrorCode::SshInternalError;
|
default: return ErrorCode::SshInternalError;
|
||||||
|
|||||||
@@ -359,6 +359,23 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!obj.value("Jc").isNull() && !obj.value("Jmin").isNull()
|
||||||
|
&& !obj.value("Jmax").isNull() && !obj.value("S1").isNull()
|
||||||
|
&& !obj.value("S2").isNull() && !obj.value("H1").isNull()
|
||||||
|
&& !obj.value("H2").isNull() && !obj.value("H3").isNull()
|
||||||
|
&& !obj.value("H4").isNull()) {
|
||||||
|
config.m_junkPacketCount = obj.value("Jc").toString();
|
||||||
|
config.m_junkPacketMinSize = obj.value("Jmin").toString();
|
||||||
|
config.m_junkPacketMaxSize = obj.value("Jmax").toString();
|
||||||
|
config.m_initPacketJunkSize = obj.value("S1").toString();
|
||||||
|
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
||||||
|
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
||||||
|
config.m_responsePacketMagicHeader = obj.value("H2").toString();
|
||||||
|
config.m_underloadPacketMagicHeader = obj.value("H3").toString();
|
||||||
|
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,34 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||||||
out << "DNS = " << dnsServers.join(", ") << "\n";
|
out << "DNS = " << dnsServers.join(", ") << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_junkPacketCount.isNull()) {
|
||||||
|
out << "Jc = " << m_junkPacketCount << "\n";
|
||||||
|
}
|
||||||
|
if (!m_junkPacketMinSize.isNull()) {
|
||||||
|
out << "JMin = " << m_junkPacketMinSize << "\n";
|
||||||
|
}
|
||||||
|
if (!m_junkPacketMaxSize.isNull()) {
|
||||||
|
out << "JMax = " << m_junkPacketMaxSize << "\n";
|
||||||
|
}
|
||||||
|
if (!m_initPacketJunkSize.isNull()) {
|
||||||
|
out << "S1 = " << m_initPacketJunkSize << "\n";
|
||||||
|
}
|
||||||
|
if (!m_responsePacketJunkSize.isNull()) {
|
||||||
|
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
||||||
|
}
|
||||||
|
if (!m_initPacketMagicHeader.isNull()) {
|
||||||
|
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
if (!m_responsePacketMagicHeader.isNull()) {
|
||||||
|
out << "H2 = " << m_responsePacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
if (!m_underloadPacketMagicHeader.isNull()) {
|
||||||
|
out << "H3 = " << m_underloadPacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
if (!m_transportPacketMagicHeader.isNull()) {
|
||||||
|
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
// If any extra config was provided, append it now.
|
// If any extra config was provided, append it now.
|
||||||
for (const QString& key : extra.keys()) {
|
for (const QString& key : extra.keys()) {
|
||||||
out << key << " = " << extra[key] << "\n";
|
out << key << " = " << extra[key] << "\n";
|
||||||
|
|||||||
@@ -40,6 +40,16 @@ class InterfaceConfig {
|
|||||||
QString m_installationId;
|
QString m_installationId;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QString m_junkPacketCount;
|
||||||
|
QString m_junkPacketMinSize;
|
||||||
|
QString m_junkPacketMaxSize;
|
||||||
|
QString m_initPacketJunkSize;
|
||||||
|
QString m_responsePacketJunkSize;
|
||||||
|
QString m_initPacketMagicHeader;
|
||||||
|
QString m_responsePacketMagicHeader;
|
||||||
|
QString m_underloadPacketMagicHeader;
|
||||||
|
QString m_transportPacketMagicHeader;
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
QString toWgConf(
|
QString toWgConf(
|
||||||
const QMap<QString, QString>& extra = QMap<QString, QString>()) const;
|
const QMap<QString, QString>& extra = QMap<QString, QString>()) const;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M15 9L9 15" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9 9L15 15" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 518 B |
@@ -58,7 +58,7 @@ target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
|
|||||||
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||||
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources)
|
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/awg-apple/Sources)
|
||||||
|
|
||||||
target_sources(networkextension PRIVATE
|
target_sources(networkextension PRIVATE
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/wireguard-apple/Sources/WireGuardKitGo/wireguard.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||||
#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "macos/gobridge/wireguard.h"
|
#include "macos/gobridge/wireguard.h"
|
||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
#include "3rd/ShadowSocks/ShadowSocks/ShadowSocks.h"
|
#include "3rd/ShadowSocks/ShadowSocks/ShadowSocks.h"
|
||||||
#include "platforms/ios/ssconnectivity.h"
|
#include "platforms/ios/ssconnectivity.h"
|
||||||
#include "platforms/ios/iosopenvpn2ssadapter.h"
|
#include "platforms/ios/iosopenvpn2ssadapter.h"
|
||||||
|
|||||||
+5
-1
@@ -26,6 +26,11 @@ int main(int argc, char *argv[])
|
|||||||
AllowSetForegroundWindow(ASFW_ANY);
|
AllowSetForegroundWindow(ASFW_ANY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// QTBUG-95974 QTBUG-95764 QTBUG-102168
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
qputenv("QT_ANDROID_DISABLE_ACCESSIBILITY", "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
#else
|
#else
|
||||||
@@ -53,7 +58,6 @@ int main(int argc, char *argv[])
|
|||||||
app.setOrganizationName(ORGANIZATION_NAME);
|
app.setOrganizationName(ORGANIZATION_NAME);
|
||||||
app.setApplicationDisplayName(APPLICATION_NAME);
|
app.setApplicationDisplayName(APPLICATION_NAME);
|
||||||
|
|
||||||
app.loadTranslator();
|
|
||||||
app.loadFonts();
|
app.loadFonts();
|
||||||
|
|
||||||
bool doExec = app.parseCommands();
|
bool doExec = app.parseCommands();
|
||||||
|
|||||||
@@ -115,7 +115,13 @@ void LocalSocketController::daemonConnected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||||
QJsonObject wgConfig = rawConfig.value("wireguard_config_data").toObject();
|
|
||||||
|
QString protocolName = rawConfig.value("protocol").toString();
|
||||||
|
|
||||||
|
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
|
||||||
|
QJsonArray splitTunnelSites = rawConfig.value("splitTunnelSites").toArray();
|
||||||
|
|
||||||
|
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||||
|
|
||||||
QJsonObject json;
|
QJsonObject json;
|
||||||
json.insert("type", "activate");
|
json.insert("type", "activate");
|
||||||
@@ -135,23 +141,79 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
|
|
||||||
QJsonArray jsAllowedIPAddesses;
|
QJsonArray jsAllowedIPAddesses;
|
||||||
|
|
||||||
QJsonObject range_ipv4;
|
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
|
||||||
range_ipv4.insert("address", "0.0.0.0");
|
QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(","));
|
||||||
range_ipv4.insert("range", 0);
|
|
||||||
range_ipv4.insert("isIpv6", false);
|
|
||||||
jsAllowedIPAddesses.append(range_ipv4);
|
|
||||||
|
|
||||||
QJsonObject range_ipv6;
|
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
|
||||||
range_ipv6.insert("address", "::");
|
// Use AllowedIP list from WG config bacouse of higer priority
|
||||||
range_ipv6.insert("range", 0);
|
|
||||||
range_ipv6.insert("isIpv6", true);
|
for (auto v : plainAllowedIP) {
|
||||||
jsAllowedIPAddesses.append(range_ipv6);
|
QString ipRange = v.toString();
|
||||||
|
qDebug() << "ipRange " << ipRange;
|
||||||
|
if (ipRange.split('/').size() > 1){
|
||||||
|
QJsonObject range;
|
||||||
|
range.insert("address", ipRange.split('/')[0]);
|
||||||
|
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
|
||||||
|
range.insert("isIpv6", false);
|
||||||
|
jsAllowedIPAddesses.append(range);
|
||||||
|
} else {
|
||||||
|
QJsonObject range;
|
||||||
|
range.insert("address",ipRange);
|
||||||
|
range.insert("range", 32);
|
||||||
|
range.insert("isIpv6", false);
|
||||||
|
jsAllowedIPAddesses.append(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Use APP split tunnel
|
||||||
|
if (splitTunnelType == 0 || splitTunnelType == 2) {
|
||||||
|
QJsonObject range_ipv4;
|
||||||
|
range_ipv4.insert("address", "0.0.0.0");
|
||||||
|
range_ipv4.insert("range", 0);
|
||||||
|
range_ipv4.insert("isIpv6", false);
|
||||||
|
jsAllowedIPAddesses.append(range_ipv4);
|
||||||
|
|
||||||
|
QJsonObject range_ipv6;
|
||||||
|
range_ipv6.insert("address", "::");
|
||||||
|
range_ipv6.insert("range", 0);
|
||||||
|
range_ipv6.insert("isIpv6", true);
|
||||||
|
jsAllowedIPAddesses.append(range_ipv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitTunnelType == 1) {
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
QString ipRange = v.toString();
|
||||||
|
qDebug() << "ipRange " << ipRange;
|
||||||
|
if (ipRange.split('/').size() > 1){
|
||||||
|
QJsonObject range;
|
||||||
|
range.insert("address", ipRange.split('/')[0]);
|
||||||
|
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
|
||||||
|
range.insert("isIpv6", false);
|
||||||
|
jsAllowedIPAddesses.append(range);
|
||||||
|
} else {
|
||||||
|
QJsonObject range;
|
||||||
|
range.insert("address",ipRange);
|
||||||
|
range.insert("range", 32);
|
||||||
|
range.insert("isIpv6", false);
|
||||||
|
jsAllowedIPAddesses.append(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||||
|
|
||||||
|
|
||||||
QJsonArray jsExcludedAddresses;
|
QJsonArray jsExcludedAddresses;
|
||||||
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
||||||
|
if (splitTunnelType == 2) {
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
QString ipRange = v.toString();
|
||||||
|
jsExcludedAddresses.append(ipRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||||
|
|
||||||
|
|
||||||
@@ -160,6 +222,19 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
// splitTunnelApps.append(QJsonValue(uri));
|
// splitTunnelApps.append(QJsonValue(uri));
|
||||||
// }
|
// }
|
||||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||||
|
|
||||||
|
if (protocolName == amnezia::config_key::awg) {
|
||||||
|
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::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));
|
||||||
|
}
|
||||||
|
|
||||||
write(json);
|
write(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ struct MessageKey
|
|||||||
static const char *host;
|
static const char *host;
|
||||||
static const char *port;
|
static const char *port;
|
||||||
static const char *isOnDemand;
|
static const char *isOnDemand;
|
||||||
|
static const char *SplitTunnelType;
|
||||||
|
static const char *SplitTunnelSites;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IosController : public QObject
|
class IosController : public QObject
|
||||||
@@ -62,6 +64,7 @@ private:
|
|||||||
bool setupOpenVPN();
|
bool setupOpenVPN();
|
||||||
bool setupCloak();
|
bool setupCloak();
|
||||||
bool setupWireGuard();
|
bool setupWireGuard();
|
||||||
|
bool setupAwg();
|
||||||
|
|
||||||
bool startOpenVPN(const QString &config);
|
bool startOpenVPN(const QString &config);
|
||||||
bool startWireGuard(const QString &jsonConfig);
|
bool startWireGuard(const QString &jsonConfig);
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ const char* MessageKey::errorCode = "errorCode";
|
|||||||
const char* MessageKey::host = "host";
|
const char* MessageKey::host = "host";
|
||||||
const char* MessageKey::port = "port";
|
const char* MessageKey::port = "port";
|
||||||
const char* MessageKey::isOnDemand = "is-on-demand";
|
const char* MessageKey::isOnDemand = "is-on-demand";
|
||||||
|
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||||
|
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||||
|
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -204,6 +207,9 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
|||||||
if (proto == amnezia::Proto::WireGuard) {
|
if (proto == amnezia::Proto::WireGuard) {
|
||||||
return setupWireGuard();
|
return setupWireGuard();
|
||||||
}
|
}
|
||||||
|
if (proto == amnezia::Proto::Awg) {
|
||||||
|
return setupAwg();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -307,6 +313,15 @@ bool IosController::setupWireGuard()
|
|||||||
return startWireGuard(wgConfig);
|
return startWireGuard(wgConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IosController::setupAwg()
|
||||||
|
{
|
||||||
|
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
|
||||||
|
|
||||||
|
QString wgConfig = config[config_key::config].toString();
|
||||||
|
|
||||||
|
return startWireGuard(wgConfig);
|
||||||
|
}
|
||||||
|
|
||||||
bool IosController::startOpenVPN(const QString &config)
|
bool IosController::startOpenVPN(const QString &config)
|
||||||
{
|
{
|
||||||
qDebug() << "IosController::startOpenVPN";
|
qDebug() << "IosController::startOpenVPN";
|
||||||
@@ -339,6 +354,13 @@ void IosController::startTunnel()
|
|||||||
{
|
{
|
||||||
m_rxBytes = 0;
|
m_rxBytes = 0;
|
||||||
m_txBytes = 0;
|
m_txBytes = 0;
|
||||||
|
|
||||||
|
int STT = m_rawConfig["splitTunnelType"].toInt();
|
||||||
|
QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray();
|
||||||
|
QJsonDocument doc;
|
||||||
|
doc.setArray(splitTunnelSites);
|
||||||
|
QString STS(doc.toJson());
|
||||||
|
|
||||||
[m_currentTunnel setEnabled:YES];
|
[m_currentTunnel setEnabled:YES];
|
||||||
|
|
||||||
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
|
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
|
||||||
@@ -364,8 +386,15 @@ void IosController::startTunnel()
|
|||||||
NSString *actionValue = [NSString stringWithUTF8String:Action::start];
|
NSString *actionValue = [NSString stringWithUTF8String:Action::start];
|
||||||
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
|
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
|
||||||
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
|
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
|
||||||
|
NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType];
|
||||||
|
NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT];
|
||||||
|
NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites];
|
||||||
|
NSString *SplitTunnelSitesValue = STS.toNSString();
|
||||||
|
|
||||||
|
|
||||||
|
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue,
|
||||||
|
SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue};
|
||||||
|
|
||||||
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue};
|
|
||||||
sendVpnExtensionMessage(message);
|
sendVpnExtensionMessage(message);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ struct Constants {
|
|||||||
static let kMessageKeyHost = "host"
|
static let kMessageKeyHost = "host"
|
||||||
static let kMessageKeyPort = "port"
|
static let kMessageKeyPort = "port"
|
||||||
static let kMessageKeyOnDemand = "is-on-demand"
|
static let kMessageKeyOnDemand = "is-on-demand"
|
||||||
|
static let kMessageKeySplitTunnelType = "SplitTunnelType"
|
||||||
|
static let kMessageKeySplitTunnelSites = "SplitTunnelSites"
|
||||||
}
|
}
|
||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
@@ -49,6 +51,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
||||||
|
|
||||||
private var openVPNConfig: Data? = nil
|
private var openVPNConfig: Data? = nil
|
||||||
|
private var SplitTunnelType: String? = nil
|
||||||
|
private var SplitTunnelSites: String? = nil
|
||||||
|
|
||||||
let vpnReachability = OpenVPNReachability()
|
let vpnReachability = OpenVPNReachability()
|
||||||
|
|
||||||
@@ -63,6 +67,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
|
|
||||||
|
let tmpStr = String(data: messageData, encoding: .utf8)!
|
||||||
|
wg_log(.error, message: tmpStr)
|
||||||
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
|
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
|
||||||
Logger.global?.log(message: "Failed to serialize message from app")
|
Logger.global?.log(message: "Failed to serialize message from app")
|
||||||
return
|
return
|
||||||
@@ -83,6 +90,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if action == Constants.kActionStart {
|
||||||
|
SplitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String
|
||||||
|
SplitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String
|
||||||
|
}
|
||||||
|
|
||||||
let callbackWrapper: (NSNumber?) -> Void = { errorCode in
|
let callbackWrapper: (NSNumber?) -> Void = { errorCode in
|
||||||
//let tunnelId = self.tunnelConfig?.id ?? ""
|
//let tunnelId = self.tunnelConfig?.id ?? ""
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
@@ -118,8 +130,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
switch self.protoType {
|
switch self.protoType {
|
||||||
case .wireguard:
|
case .wireguard:
|
||||||
self.startWireguard(activationAttemptId: activationAttemptId,
|
self.startWireguard(activationAttemptId: activationAttemptId,
|
||||||
errorNotifier: errorNotifier,
|
errorNotifier: errorNotifier,
|
||||||
completionHandler: completionHandler)
|
completionHandler: completionHandler)
|
||||||
case .openvpn:
|
case .openvpn:
|
||||||
self.startOpenVPN(completionHandler: completionHandler)
|
self.startOpenVPN(completionHandler: completionHandler)
|
||||||
case .shadowsocks:
|
case .shadowsocks:
|
||||||
@@ -156,7 +168,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
||||||
case .shadowsocks:
|
case .shadowsocks:
|
||||||
break
|
break
|
||||||
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
|
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -168,12 +180,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
errorNotifier: ErrorNotifier,
|
errorNotifier: ErrorNotifier,
|
||||||
completionHandler: @escaping (Error?) -> Void) {
|
completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
||||||
wg_log(.error, message: "Can't start WireGuard config missing")
|
wg_log(.error, message: "Can't start WireGuard config missing")
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
|
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
|
||||||
|
|
||||||
@@ -183,6 +196,48 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ") == "0.0.0.0/0, ::/0") {
|
||||||
|
if (SplitTunnelType == "1") {
|
||||||
|
for index in tunnelConfiguration.peers.indices {
|
||||||
|
tunnelConfiguration.peers[index].allowedIPs.removeAll()
|
||||||
|
var allowedIPs = [IPAddressRange]()
|
||||||
|
let STSdata = Data(SplitTunnelSites!.utf8)
|
||||||
|
do {
|
||||||
|
let STSArray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
|
||||||
|
for allowedIPString in STSArray {
|
||||||
|
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||||
|
allowedIPs.append(allowedIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
wg_log(.error,message: "Parse JSONSerialization Error")
|
||||||
|
}
|
||||||
|
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SplitTunnelType == "2")
|
||||||
|
{
|
||||||
|
for index in tunnelConfiguration.peers.indices {
|
||||||
|
var excludeIPs = [IPAddressRange]()
|
||||||
|
let STSdata = Data(SplitTunnelSites!.utf8)
|
||||||
|
do {
|
||||||
|
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
|
||||||
|
for excludeIPString in STSarray {
|
||||||
|
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||||
|
excludeIPs.append(excludeIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
wg_log(.error,message: "Parse JSONSerialization Error")
|
||||||
|
}
|
||||||
|
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
||||||
|
|
||||||
// Start the tunnel
|
// Start the tunnel
|
||||||
@@ -226,10 +281,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
||||||
// TODO: handle errors properly
|
// TODO: handle errors properly
|
||||||
wg_log(.error, message: "Can't start startOpenVPN()")
|
wg_log(.error, message: "Can't start startOpenVPN()")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,8 +333,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
for component in components{
|
for component in components{
|
||||||
let pair = component.components(separatedBy: "=")
|
let pair = component.components(separatedBy: "=")
|
||||||
if pair.count == 2 {
|
if pair.count == 2 {
|
||||||
settingsDictionary[pair[0]] = pair[1]
|
settingsDictionary[pair[0]] = pair[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
@@ -337,15 +392,15 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
guard let completionHandler = completionHandler else { return }
|
guard let completionHandler = completionHandler else { return }
|
||||||
let bytesin = ovpnAdapter.transportStatistics.bytesIn
|
let bytesin = ovpnAdapter.transportStatistics.bytesIn
|
||||||
let bytesout = ovpnAdapter.transportStatistics.bytesOut
|
let bytesout = ovpnAdapter.transportStatistics.bytesOut
|
||||||
|
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
"rx_bytes" : bytesin,
|
"rx_bytes" : bytesin,
|
||||||
"tx_bytes" : bytesout
|
"tx_bytes" : bytesout
|
||||||
]
|
]
|
||||||
|
|
||||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -382,11 +437,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
startHandler = completionHandler
|
startHandler = completionHandler
|
||||||
ovpnAdapter.connect(using: packetFlow)
|
ovpnAdapter.connect(using: packetFlow)
|
||||||
|
|
||||||
// let ifaces = Interface.allInterfaces()
|
// let ifaces = Interface.allInterfaces()
|
||||||
// .filter { $0.family == .ipv4 }
|
// .filter { $0.family == .ipv4 }
|
||||||
// .map { iface in iface.name }
|
// .map { iface in iface.name }
|
||||||
|
|
||||||
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -- Network observing methods
|
// MARK: -- Network observing methods
|
||||||
@@ -401,9 +456,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?,
|
override func observeValue(forKeyPath keyPath: String?,
|
||||||
of object: Any?,
|
of object: Any?,
|
||||||
change: [NSKeyValueChangeKey : Any]?,
|
change: [NSKeyValueChangeKey : Any]?,
|
||||||
context: UnsafeMutableRawPointer?) {
|
context: UnsafeMutableRawPointer?) {
|
||||||
guard Constants.kDefaultPathKey != keyPath else { return }
|
guard Constants.kDefaultPathKey != keyPath else { return }
|
||||||
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
|
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
|
||||||
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
|
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
|
||||||
@@ -412,8 +467,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
|
guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
|
||||||
let defPath = defaultPath,
|
let defPath = defaultPath,
|
||||||
lastPath != defPath || lastPath.description != defPath.description else {
|
lastPath != defPath || lastPath.description != defPath.description else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let `self` = self, self.defaultPath != nil else { return }
|
guard let `self` = self, self.defaultPath != nil else { return }
|
||||||
self.handle(networkChange: self.defaultPath!) { _ in }
|
self.handle(networkChange: self.defaultPath!) { _ in }
|
||||||
@@ -478,6 +533,50 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||||||
// send empty string to NEDNSSettings.matchDomains
|
// send empty string to NEDNSSettings.matchDomains
|
||||||
networkSettings?.dnsSettings?.matchDomains = [""]
|
networkSettings?.dnsSettings?.matchDomains = [""]
|
||||||
|
|
||||||
|
if (SplitTunnelType == "1") {
|
||||||
|
var ipv4IncludedRoutes = [NEIPv4Route]()
|
||||||
|
let STSdata = Data(SplitTunnelSites!.utf8)
|
||||||
|
do {
|
||||||
|
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
|
||||||
|
for allowedIPString in STSarray {
|
||||||
|
if let allowedIP = IPAddressRange(from: allowedIPString){
|
||||||
|
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allowedIP.address)", subnetMask: "\(allowedIP.subnetMask())"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
wg_log(.error,message: "Parse JSONSerialization Error")
|
||||||
|
}
|
||||||
|
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
|
||||||
|
} else {
|
||||||
|
if (SplitTunnelType == "2")
|
||||||
|
{
|
||||||
|
var ipv4ExcludedRoutes = [NEIPv4Route]()
|
||||||
|
var ipv4IncludedRoutes = [NEIPv4Route]()
|
||||||
|
var ipv6IncludedRoutes = [NEIPv6Route]()
|
||||||
|
let STSdata = Data(SplitTunnelSites!.utf8)
|
||||||
|
do {
|
||||||
|
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
|
||||||
|
for excludeIPString in STSarray {
|
||||||
|
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||||
|
ipv4ExcludedRoutes.append(NEIPv4Route(destinationAddress: "\(excludeIP.address)", subnetMask: "\(excludeIP.subnetMask())"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
wg_log(.error,message: "Parse JSONSerialization Error")
|
||||||
|
}
|
||||||
|
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0"){
|
||||||
|
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allIPv4.address)", subnetMask: "\(allIPv4.subnetMask())"))
|
||||||
|
}
|
||||||
|
if let allIPv6 = IPAddressRange(from: "::/0") {
|
||||||
|
ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(allIPv6.address)", networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength)))
|
||||||
|
}
|
||||||
|
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
|
||||||
|
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
|
||||||
|
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Set the network settings for the current tunneling session.
|
// Set the network settings for the current tunneling session.
|
||||||
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
|
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,15 +158,15 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
|
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
|
||||||
|
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtm->rtm_type == RTN_THROW) {
|
if (rtm->rtm_type == RTN_THROW) {
|
||||||
int index = if_nametoindex(getgatewayandiface().toUtf8());
|
struct in_addr ip4;
|
||||||
if (index <= 0) {
|
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4);
|
||||||
logger.error() << "if_nametoindex() failed:" << strerror(errno);
|
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
|
||||||
return false;
|
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
|
||||||
}
|
rtm->rtm_type = RTN_UNICAST;
|
||||||
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_nl nladdr;
|
struct sockaddr_nl nladdr;
|
||||||
@@ -334,7 +334,7 @@ QString LinuxRouteMonitor::getgatewayandiface()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(sock);
|
close(sock);
|
||||||
return interface;
|
return gateway_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool buildAllowedIp(wg_allowedip* ip,
|
static bool buildAllowedIp(wg_allowedip* ip,
|
||||||
|
|||||||
@@ -100,6 +100,19 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
|||||||
QTextStream out(&message);
|
QTextStream out(&message);
|
||||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||||
out << "replace_peers=true\n";
|
out << "replace_peers=true\n";
|
||||||
|
|
||||||
|
if (config.m_junkPacketCount != "") {
|
||||||
|
out << "jc=" << config.m_junkPacketCount << "\n";
|
||||||
|
out << "jmin=" << config.m_junkPacketMinSize << "\n";
|
||||||
|
out << "jmax=" << config.m_junkPacketMaxSize << "\n";
|
||||||
|
out << "s1=" << config.m_initPacketJunkSize << "\n";
|
||||||
|
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||||
|
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||||
|
out << "h2=" << config.m_responsePacketMagicHeader << "\n";
|
||||||
|
out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
|
||||||
|
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
int err = uapiErrno(uapiCommand(message));
|
int err = uapiErrno(uapiCommand(message));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||||
|
|||||||
@@ -100,6 +100,19 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||||||
QTextStream out(&message);
|
QTextStream out(&message);
|
||||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||||
out << "replace_peers=true\n";
|
out << "replace_peers=true\n";
|
||||||
|
|
||||||
|
if (config.m_junkPacketCount != "") {
|
||||||
|
out << "jc=" << config.m_junkPacketCount << "\n";
|
||||||
|
out << "jmin=" << config.m_junkPacketMinSize << "\n";
|
||||||
|
out << "jmax=" << config.m_junkPacketMaxSize << "\n";
|
||||||
|
out << "s1=" << config.m_initPacketJunkSize << "\n";
|
||||||
|
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||||
|
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||||
|
out << "h2=" << config.m_responsePacketMagicHeader << "\n";
|
||||||
|
out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
|
||||||
|
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
int err = uapiErrno(uapiCommand(message));
|
int err = uapiErrno(uapiCommand(message));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||||
|
|||||||
@@ -236,6 +236,17 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config.m_excludedAddresses.empty()) {
|
||||||
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
|
logger.debug() << "range: " << i;
|
||||||
|
|
||||||
|
if (!allowTrafficToRange(i, HIGH_WEIGHT,
|
||||||
|
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
|
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
|
||||||
@@ -411,8 +422,8 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||||
int weight, const QString& title,
|
int weight, const QString& title,
|
||||||
const QString& peer) {
|
const QString& peer) {
|
||||||
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
|
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
|
||||||
GUID layerOut =
|
GUID layerOut =
|
||||||
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
@@ -473,6 +484,57 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficToRange(const IPAddress& addr, uint8_t weight,
|
||||||
|
const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
QString description("Allow traffic %1 %2 ");
|
||||||
|
|
||||||
|
auto lower = addr.address();
|
||||||
|
auto upper = addr.broadcastAddress();
|
||||||
|
|
||||||
|
const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol;
|
||||||
|
const GUID layerKeyOut =
|
||||||
|
isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
|
||||||
|
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
|
FWPM_FILTER_CONDITION0 cond[1] = {0};
|
||||||
|
FWP_RANGE0 ipRange;
|
||||||
|
QByteArray lowIpV6Buffer;
|
||||||
|
QByteArray highIpV6Buffer;
|
||||||
|
|
||||||
|
importAddress(lower, ipRange.valueLow, &lowIpV6Buffer);
|
||||||
|
importAddress(upper, ipRange.valueHigh, &highIpV6Buffer);
|
||||||
|
|
||||||
|
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
||||||
|
cond[0].matchType = FWP_MATCH_RANGE;
|
||||||
|
cond[0].conditionValue.type = FWP_RANGE_TYPE;
|
||||||
|
cond[0].conditionValue.rangeValue = &ipRange;
|
||||||
|
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.filterCondition = cond;
|
||||||
|
|
||||||
|
filter.layerKey = layerKeyOut;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
|
||||||
|
peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = layerKeyIn;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("from").arg(addr.toString()), peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||||
// Allow outbound DHCPv4
|
// Allow outbound DHCPv4
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ class WindowsFirewall final : public QObject {
|
|||||||
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
||||||
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
||||||
const QString& title, const QString& peer = QString());
|
const QString& title, const QString& peer = QString());
|
||||||
|
bool allowTrafficToRange(const IPAddress& addr, uint8_t weight,
|
||||||
|
const QString& title,
|
||||||
|
const QString& peer);
|
||||||
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||||
const QString& title);
|
const QString& title);
|
||||||
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include "awgprotocol.h"
|
||||||
|
|
||||||
|
Awg::Awg(const QJsonObject &configuration, QObject *parent)
|
||||||
|
: WireguardProtocol(configuration, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Awg::~Awg()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef AWGPROTOCOL_H
|
||||||
|
#define AWGPROTOCOL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "wireguardprotocol.h"
|
||||||
|
|
||||||
|
class Awg : public WireguardProtocol
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Awg(const QJsonObject &configuration, QObject *parent = nullptr);
|
||||||
|
virtual ~Awg() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AWGPROTOCOL_H
|
||||||
@@ -22,6 +22,8 @@ OpenVpnOverCloakProtocol::~OpenVpnOverCloakProtocol()
|
|||||||
|
|
||||||
ErrorCode OpenVpnOverCloakProtocol::start()
|
ErrorCode OpenVpnOverCloakProtocol::start()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (!QFileInfo::exists(cloakExecPath())) {
|
if (!QFileInfo::exists(cloakExecPath())) {
|
||||||
setLastError(ErrorCode::CloakExecutableMissing);
|
setLastError(ErrorCode::CloakExecutableMissing);
|
||||||
return lastError();
|
return lastError();
|
||||||
@@ -77,10 +79,12 @@ ErrorCode OpenVpnOverCloakProtocol::start()
|
|||||||
|
|
||||||
if (m_ckProcess.state() == QProcess::ProcessState::Running) {
|
if (m_ckProcess.state() == QProcess::ProcessState::Running) {
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
#endif
|
||||||
return OpenVpnProtocol::start();
|
return OpenVpnProtocol::start();
|
||||||
|
#if 0
|
||||||
}
|
}
|
||||||
else return ErrorCode::CloakExecutableMissing;
|
else return ErrorCode::CloakExecutableMissing;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVpnOverCloakProtocol::stop()
|
void OpenVpnOverCloakProtocol::stop()
|
||||||
@@ -90,16 +94,6 @@ void OpenVpnOverCloakProtocol::stop()
|
|||||||
|
|
||||||
qDebug() << "OpenVpnOverCloakProtocol::stop()";
|
qDebug() << "OpenVpnOverCloakProtocol::stop()";
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
Utils::signalCtrl(m_ckProcess.processId(), CTRL_C_EVENT);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_ckProcess.terminate();
|
|
||||||
|
|
||||||
if (Utils::processIsRunning(Utils::executable("ck-client", false))) {
|
|
||||||
QThread::msleep(1000);
|
|
||||||
Utils::killProcessByName(Utils::executable("ck-client", false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OpenVpnOverCloakProtocol::cloakExecPath()
|
QString OpenVpnOverCloakProtocol::cloakExecPath()
|
||||||
|
|||||||
@@ -10,11 +10,10 @@
|
|||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
|
||||||
OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
||||||
{
|
{
|
||||||
readOpenVpnConfiguration(configuration);
|
readOpenVpnConfiguration(configuration);
|
||||||
connect(&m_managementServer, &ManagementServer::readyRead, this,
|
|
||||||
&OpenVpnProtocol::onReadyReadDataFromManagementServer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenVpnProtocol::~OpenVpnProtocol()
|
OpenVpnProtocol::~OpenVpnProtocol()
|
||||||
@@ -25,7 +24,6 @@ OpenVpnProtocol::~OpenVpnProtocol()
|
|||||||
|
|
||||||
QString OpenVpnProtocol::defaultConfigFileName()
|
QString OpenVpnProtocol::defaultConfigFileName()
|
||||||
{
|
{
|
||||||
// qDebug() << "OpenVpnProtocol::defaultConfigFileName" << defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
|
|
||||||
return defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
|
return defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,25 +31,20 @@ QString OpenVpnProtocol::defaultConfigPath()
|
|||||||
{
|
{
|
||||||
QString p = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config";
|
QString p = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config";
|
||||||
Utils::initializePath(p);
|
Utils::initializePath(p);
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVpnProtocol::stop()
|
void OpenVpnProtocol::stop()
|
||||||
{
|
{
|
||||||
qDebug() << "OpenVpnProtocol::stop()";
|
qDebug() << "OpenVpnProtocol::stop()";
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
|
||||||
|
|
||||||
// TODO: need refactoring
|
// TODO: need refactoring
|
||||||
// sendTermSignal() will even return true while server connected ???
|
// sendTermSignal() will even return true while server connected ???
|
||||||
if ((m_connectionState == Vpn::ConnectionState::Preparing) || (m_connectionState == Vpn::ConnectionState::Connecting)
|
if ((m_connectionState == Vpn::ConnectionState::Preparing) || (m_connectionState == Vpn::ConnectionState::Connecting)
|
||||||
|| (m_connectionState == Vpn::ConnectionState::Connected)
|
|| (m_connectionState == Vpn::ConnectionState::Connected)
|
||||||
|| (m_connectionState == Vpn::ConnectionState::Reconnecting)) {
|
|| (m_connectionState == Vpn::ConnectionState::Reconnecting)) {
|
||||||
if (!sendTermSignal()) {
|
|
||||||
killOpenVpnProcess();
|
killOpenVpnProcess();
|
||||||
}
|
QThread::msleep(10);
|
||||||
QThread::msleep(10);
|
|
||||||
m_managementServer.stop();
|
|
||||||
}
|
}
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
}
|
}
|
||||||
@@ -86,9 +79,30 @@ void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration)
|
|||||||
{
|
{
|
||||||
if (configuration.contains(ProtocolProps::key_proto_config_data(Proto::OpenVpn))) {
|
if (configuration.contains(ProtocolProps::key_proto_config_data(Proto::OpenVpn))) {
|
||||||
QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::OpenVpn)).toObject();
|
QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::OpenVpn)).toObject();
|
||||||
|
QString plainConfig = jConfig.value(config_key::config).toString().toUtf8();
|
||||||
|
if (configuration.contains(ProtocolProps::key_proto_config_data(Proto::Cloak))) {
|
||||||
|
QJsonObject cloakConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::Cloak)).toObject();
|
||||||
|
cloakConfig["NumConn"] = 1;
|
||||||
|
cloakConfig["ProxyMethod"] = "openvpn";
|
||||||
|
if (cloakConfig.contains("port")) {
|
||||||
|
int portValue = cloakConfig.value("port").toInt();
|
||||||
|
cloakConfig.remove("port");
|
||||||
|
cloakConfig["RemotePort"] = portValue;
|
||||||
|
}
|
||||||
|
if (cloakConfig.contains("remote")) {
|
||||||
|
QString hostValue = cloakConfig.value("remote").toString();
|
||||||
|
cloakConfig.remove("remote");
|
||||||
|
cloakConfig["RemoteHost"] = hostValue;
|
||||||
|
}
|
||||||
|
plainConfig += "\n<cloak>\n";
|
||||||
|
QJsonDocument Doc(cloakConfig);
|
||||||
|
QByteArray ba = Doc.toJson();
|
||||||
|
QString plainCloak = ba;
|
||||||
|
plainConfig += QString::fromLatin1(plainCloak.toUtf8().toBase64().data());
|
||||||
|
plainConfig += "\n</cloak>\n";
|
||||||
|
}
|
||||||
m_configFile.open();
|
m_configFile.open();
|
||||||
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
|
m_configFile.write(plainConfig.toUtf8());
|
||||||
m_configFile.close();
|
m_configFile.close();
|
||||||
m_configFileName = m_configFile.fileName();
|
m_configFileName = m_configFile.fileName();
|
||||||
|
|
||||||
@@ -98,7 +112,7 @@ void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration)
|
|||||||
|
|
||||||
bool OpenVpnProtocol::openVpnProcessIsRunning() const
|
bool OpenVpnProtocol::openVpnProcessIsRunning() const
|
||||||
{
|
{
|
||||||
return Utils::processIsRunning("openvpn");
|
return Utils::processIsRunning("ovpncli");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVpnProtocol::disconnectFromManagementServer()
|
void OpenVpnProtocol::disconnectFromManagementServer()
|
||||||
@@ -122,7 +136,6 @@ void OpenVpnProtocol::sendManagementCommand(const QString &command)
|
|||||||
|
|
||||||
uint OpenVpnProtocol::selectMgmtPort()
|
uint OpenVpnProtocol::selectMgmtPort()
|
||||||
{
|
{
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
quint32 port = QRandomGenerator::global()->generate();
|
quint32 port = QRandomGenerator::global()->generate();
|
||||||
port = (double)(65000 - 15001) * port / UINT32_MAX + 15001;
|
port = (double)(65000 - 15001) * port / UINT32_MAX + 15001;
|
||||||
@@ -132,26 +145,63 @@ uint OpenVpnProtocol::selectMgmtPort()
|
|||||||
if (ok)
|
if (ok)
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_managementPort;
|
return m_managementPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVpnProtocol::updateRouteGateway(QString line)
|
void OpenVpnProtocol::updateRouteGateway(QString line)
|
||||||
{
|
{
|
||||||
// TODO: fix for macos
|
const QString substr = "sitnl_route_best_gw result: via ";
|
||||||
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
|
int start = line.indexOf(substr) + substr.size();
|
||||||
if (!line.contains("/"))
|
int end = line.indexOf(" dev ", start);
|
||||||
return;
|
|
||||||
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
|
m_routeGateway = line.mid(start, (end-start));
|
||||||
m_routeGateway.replace(" ", "");
|
|
||||||
qDebug() << "Set VPN route gateway" << m_routeGateway;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenVpnProtocol::handle_cli_message(QString message)
|
||||||
|
{
|
||||||
|
QString line = message;
|
||||||
|
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.contains("EVENT: CONNECTED")) {
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connected);
|
||||||
|
} else if (line.contains("EXITING")) {
|
||||||
|
// openVpnStateSigTermHandler();
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||||
|
} else if (line.contains("RECONNECTING")) {
|
||||||
|
setConnectionState(Vpn::ConnectionState::Reconnecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.contains("sitnl_route_best_gw")) {
|
||||||
|
updateRouteGateway(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.contains("[ifconfig]")) {
|
||||||
|
updateVpnGateway(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: SET CORRECT STRING
|
||||||
|
if (line.contains("FATAL")) {
|
||||||
|
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
|
||||||
|
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
|
||||||
|
} else {
|
||||||
|
emit protocolError(ErrorCode::OpenVpnUnknownError);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ErrorCode OpenVpnProtocol::start()
|
ErrorCode OpenVpnProtocol::start()
|
||||||
{
|
{
|
||||||
// qDebug() << "Start OpenVPN connection";
|
// qDebug() << "Start OpenVPN connection";
|
||||||
OpenVpnProtocol::stop();
|
OpenVpnProtocol::stop();
|
||||||
|
|
||||||
|
qDebug() << " Utils::openVpnExecPath();" << Utils::openVpnExecPath();
|
||||||
|
|
||||||
if (!QFileInfo::exists(Utils::openVpnExecPath())) {
|
if (!QFileInfo::exists(Utils::openVpnExecPath())) {
|
||||||
setLastError(ErrorCode::OpenVpnExecutableMissing);
|
setLastError(ErrorCode::OpenVpnExecutableMissing);
|
||||||
return lastError();
|
return lastError();
|
||||||
@@ -184,23 +234,11 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
|
|
||||||
// Utils::createEmptyFile(vpnLogFileNamePath);
|
|
||||||
|
|
||||||
uint mgmtPort = selectMgmtPort();
|
|
||||||
qDebug() << "OpenVpnProtocol::start mgmt port selected:" << mgmtPort;
|
|
||||||
|
|
||||||
if (!m_managementServer.start(m_managementHost, mgmtPort)) {
|
|
||||||
setLastError(ErrorCode::OpenVpnManagementServerError);
|
|
||||||
return lastError();
|
|
||||||
}
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
|
||||||
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
|
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
|
||||||
|
|
||||||
if (!m_openVpnProcess) {
|
if (!m_openVpnProcess) {
|
||||||
// qWarning() << "IpcProcess replica is not created!";
|
qWarning() << "IpcProcess replica is not created!";
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
}
|
}
|
||||||
@@ -212,122 +250,100 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
}
|
}
|
||||||
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
|
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
|
||||||
QStringList arguments({
|
QStringList arguments({ configPath()/*, "--management", m_managementHost, QString::number(mgmtPort),
|
||||||
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
|
"--management-client" *//*, "--log", vpnLogFileNamePath */
|
||||||
"--management-client" /*, "--log", vpnLogFileNamePath */
|
|
||||||
});
|
});
|
||||||
m_openVpnProcess->setArguments(arguments);
|
m_openVpnProcess->setArguments(arguments);
|
||||||
|
|
||||||
qDebug() << arguments.join(" ");
|
qDebug() << arguments.join(" ");
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
|
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
|
||||||
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
|
[&](QProcess::ProcessError error) {
|
||||||
|
qDebug() << "PrivilegedProcess errorOccurred" << error;
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged,
|
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged, [&](QProcess::ProcessState newState) {
|
||||||
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
switch ( newState )
|
||||||
|
{
|
||||||
|
case QProcess::Starting:
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
break;
|
||||||
|
case QProcess::Running:
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
}
|
||||||
|
qDebug() << "PrivilegedProcess stateChanged" << newState;
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
|
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
|
||||||
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||||
|
|
||||||
m_openVpnProcess->start();
|
|
||||||
|
|
||||||
// startTimeoutTimer();
|
connect(m_openVpnProcess.data(), &PrivilegedProcess::readyRead, this, [&] {
|
||||||
|
|
||||||
|
QRemoteObjectPendingReply<QByteArray> call = m_openVpnProcess->readAll();
|
||||||
|
auto *watcher = new QRemoteObjectPendingCallWatcher(call, this);
|
||||||
|
|
||||||
|
auto *timeoutTimer = new QTimer(this);
|
||||||
|
timeoutTimer->setSingleShot(true);
|
||||||
|
m_watchers.insert(watcher, timeoutTimer);
|
||||||
|
|
||||||
|
connect(timeoutTimer, &QTimer::timeout, this, [this, watcher, timeoutTimer]() {
|
||||||
|
qDebug() << "Foo request timed out.";
|
||||||
|
|
||||||
|
m_watchers.remove(watcher);
|
||||||
|
watcher->deleteLater();
|
||||||
|
timeoutTimer->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(watcher, &QRemoteObjectPendingCallWatcher::finished, [this](QRemoteObjectPendingCallWatcher *self) {
|
||||||
|
QTimer *timer = m_watchers.take(self);
|
||||||
|
if (timer) {
|
||||||
|
timer->stop();
|
||||||
|
timer->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray result = self->returnValue().toByteArray();
|
||||||
|
handle_cli_message(QString(result));
|
||||||
|
self->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
timeoutTimer->start(30000);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_openVpnProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&PrivilegedProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
qDebug().noquote() << "OpenVPN finished, exitCode, exiStatus" << exitCode << exitStatus;
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
if (exitStatus != QProcess::NormalExit) {
|
||||||
|
emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
if (exitCode !=0 ) {
|
||||||
|
emit protocolError(amnezia::ErrorCode::InternalError);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_openVpnProcess->start();
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenVpnProtocol::sendTermSignal()
|
|
||||||
{
|
|
||||||
return m_managementServer.writeCommand("signal SIGTERM");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenVpnProtocol::sendByteCount()
|
|
||||||
{
|
|
||||||
m_managementServer.writeCommand("bytecount 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenVpnProtocol::sendInitialData()
|
|
||||||
{
|
|
||||||
m_managementServer.writeCommand("state on");
|
|
||||||
m_managementServer.writeCommand("log on");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenVpnProtocol::onReadyReadDataFromManagementServer()
|
|
||||||
{
|
|
||||||
for (;;) {
|
|
||||||
QString line = m_managementServer.readLine().simplified();
|
|
||||||
|
|
||||||
if (line.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!line.contains(">BYTECOUNT")) {
|
|
||||||
qDebug().noquote() << line;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.contains(">INFO:OpenVPN Management Interface")) {
|
|
||||||
sendInitialData();
|
|
||||||
} else if (line.startsWith(">STATE")) {
|
|
||||||
if (line.contains("CONNECTED,SUCCESS")) {
|
|
||||||
sendByteCount();
|
|
||||||
stopTimeoutTimer();
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connected);
|
|
||||||
continue;
|
|
||||||
} else if (line.contains("EXITING,SIGTER")) {
|
|
||||||
// openVpnStateSigTermHandler();
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
|
||||||
continue;
|
|
||||||
} else if (line.contains("RECONNECTING")) {
|
|
||||||
setConnectionState(Vpn::ConnectionState::Reconnecting);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.contains("ROUTE_GATEWAY")) {
|
|
||||||
updateRouteGateway(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.contains("PUSH: Received control message")) {
|
|
||||||
updateVpnGateway(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.contains("FATAL")) {
|
|
||||||
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
|
|
||||||
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
|
|
||||||
} else {
|
|
||||||
emit protocolError(ErrorCode::OpenVpnUnknownError);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray data(line.toStdString().c_str());
|
|
||||||
if (data.contains(">BYTECOUNT:")) {
|
|
||||||
int beg = data.lastIndexOf(">BYTECOUNT:");
|
|
||||||
int end = data.indexOf("\n", beg);
|
|
||||||
|
|
||||||
beg += sizeof(">BYTECOUNT:") - 1;
|
|
||||||
QList<QByteArray> count = data.mid(beg, end - beg + 1).split(',');
|
|
||||||
|
|
||||||
quint64 r = static_cast<quint64>(count.at(0).trimmed().toULongLong());
|
|
||||||
quint64 s = static_cast<quint64>(count.at(1).trimmed().toULongLong());
|
|
||||||
|
|
||||||
setBytesChanged(r, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
||||||
{
|
{
|
||||||
// line looks like
|
// "[ifconfig] [10.8.0.14] [10.8.0.13]"
|
||||||
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart
|
QStringList params = line.split("\n");
|
||||||
// 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
|
for (const QString ¶m : params) {
|
||||||
|
if (param.contains("ifconfig")) {
|
||||||
QStringList params = line.split(",");
|
QString l = param.right(param.size() - param.indexOf("ifconfig"));
|
||||||
for (const QString &l : params) {
|
|
||||||
if (l.contains("ifconfig")) {
|
|
||||||
if (l.split(" ").size() == 3) {
|
if (l.split(" ").size() == 3) {
|
||||||
m_vpnLocalAddress = l.split(" ").at(1);
|
m_vpnLocalAddress = l.split(" ").at(1);
|
||||||
|
m_vpnLocalAddress.remove("[");m_vpnLocalAddress.remove("]");
|
||||||
m_vpnGateway = l.split(" ").at(2);
|
m_vpnGateway = l.split(" ").at(2);
|
||||||
|
m_vpnGateway.remove("[");m_vpnGateway.remove("]");
|
||||||
qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
|
qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ public:
|
|||||||
static QString defaultConfigFileName();
|
static QString defaultConfigFileName();
|
||||||
static QString defaultConfigPath();
|
static QString defaultConfigPath();
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void onReadyReadDataFromManagementServer();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString configPath() const;
|
QString configPath() const;
|
||||||
@@ -34,6 +32,7 @@ private:
|
|||||||
bool sendTermSignal();
|
bool sendTermSignal();
|
||||||
void readOpenVpnConfiguration(const QJsonObject &configuration);
|
void readOpenVpnConfiguration(const QJsonObject &configuration);
|
||||||
void disconnectFromManagementServer();
|
void disconnectFromManagementServer();
|
||||||
|
void handle_cli_message(QString message);
|
||||||
void killOpenVpnProcess();
|
void killOpenVpnProcess();
|
||||||
void sendByteCount();
|
void sendByteCount();
|
||||||
void sendInitialData();
|
void sendInitialData();
|
||||||
@@ -42,6 +41,7 @@ private:
|
|||||||
const QString m_managementHost = "127.0.0.1";
|
const QString m_managementHost = "127.0.0.1";
|
||||||
const unsigned int m_managementPort = 57775;
|
const unsigned int m_managementPort = 57775;
|
||||||
|
|
||||||
|
QHash<QRemoteObjectPendingCallWatcher*, QTimer*> m_watchers;
|
||||||
ManagementServer m_managementServer;
|
ManagementServer m_managementServer;
|
||||||
QString m_configFileName;
|
QString m_configFileName;
|
||||||
QTemporaryFile m_configFile;
|
QTemporaryFile m_configFile;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "protocols_defs.h"
|
#include "protocols_defs.h"
|
||||||
|
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Proto &p)
|
QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Proto &p)
|
||||||
@@ -66,12 +68,12 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
|
|||||||
{ Proto::ShadowSocks, "ShadowSocks" },
|
{ Proto::ShadowSocks, "ShadowSocks" },
|
||||||
{ Proto::Cloak, "Cloak" },
|
{ Proto::Cloak, "Cloak" },
|
||||||
{ Proto::WireGuard, "WireGuard" },
|
{ Proto::WireGuard, "WireGuard" },
|
||||||
|
{ Proto::Awg, "AmneziaWG" },
|
||||||
{ Proto::Ikev2, "IKEv2" },
|
{ Proto::Ikev2, "IKEv2" },
|
||||||
{ Proto::L2tp, "L2TP" },
|
{ Proto::L2tp, "L2TP" },
|
||||||
|
|
||||||
{ Proto::TorWebSite, "Website in Tor network" },
|
{ Proto::TorWebSite, "Website in Tor network" },
|
||||||
{ Proto::Dns, "DNS Service" },
|
{ Proto::Dns, "DNS Service" },
|
||||||
{ Proto::FileShare, "File Sharing Service" },
|
|
||||||
{ Proto::Sftp, QObject::tr("Sftp service") } };
|
{ Proto::Sftp, QObject::tr("Sftp service") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,27 +90,43 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
|
|||||||
case Proto::Cloak: return ServiceType::Vpn;
|
case Proto::Cloak: return ServiceType::Vpn;
|
||||||
case Proto::ShadowSocks: return ServiceType::Vpn;
|
case Proto::ShadowSocks: return ServiceType::Vpn;
|
||||||
case Proto::WireGuard: return ServiceType::Vpn;
|
case Proto::WireGuard: return ServiceType::Vpn;
|
||||||
|
case Proto::Awg: return ServiceType::Vpn;
|
||||||
|
case Proto::Ikev2: return ServiceType::Vpn;
|
||||||
|
|
||||||
case Proto::TorWebSite: return ServiceType::Other;
|
case Proto::TorWebSite: return ServiceType::Other;
|
||||||
case Proto::Dns: return ServiceType::Other;
|
case Proto::Dns: return ServiceType::Other;
|
||||||
case Proto::FileShare: return ServiceType::Other;
|
case Proto::Sftp: return ServiceType::Other;
|
||||||
default: return ServiceType::Other;
|
default: return ServiceType::Other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ProtocolProps::getPortForInstall(Proto p)
|
||||||
|
{
|
||||||
|
switch (p) {
|
||||||
|
case Awg:
|
||||||
|
case WireGuard:
|
||||||
|
case ShadowSocks:
|
||||||
|
case OpenVpn:
|
||||||
|
return QRandomGenerator::global()->bounded(30000, 50000);
|
||||||
|
default:
|
||||||
|
return defaultPort(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ProtocolProps::defaultPort(Proto p)
|
int ProtocolProps::defaultPort(Proto p)
|
||||||
{
|
{
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case Proto::Any: return -1;
|
case Proto::Any: return -1;
|
||||||
case Proto::OpenVpn: return 1194;
|
case Proto::OpenVpn: return QString(protocols::openvpn::defaultPort).toInt();
|
||||||
case Proto::Cloak: return 443;
|
case Proto::Cloak: return QString(protocols::cloak::defaultPort).toInt();
|
||||||
case Proto::ShadowSocks: return 6789;
|
case Proto::ShadowSocks: return QString(protocols::shadowsocks::defaultPort).toInt();
|
||||||
case Proto::WireGuard: return 51820;
|
case Proto::WireGuard: return QString(protocols::wireguard::defaultPort).toInt();
|
||||||
|
case Proto::Awg: return QString(protocols::awg::defaultPort).toInt();
|
||||||
case Proto::Ikev2: return -1;
|
case Proto::Ikev2: return -1;
|
||||||
case Proto::L2tp: return -1;
|
case Proto::L2tp: return -1;
|
||||||
|
|
||||||
case Proto::TorWebSite: return -1;
|
case Proto::TorWebSite: return -1;
|
||||||
case Proto::Dns: return 53;
|
case Proto::Dns: return 53;
|
||||||
case Proto::FileShare: return 139;
|
|
||||||
case Proto::Sftp: return 222;
|
case Proto::Sftp: return 222;
|
||||||
default: return -1;
|
default: return -1;
|
||||||
}
|
}
|
||||||
@@ -122,13 +140,14 @@ bool ProtocolProps::defaultPortChangeable(Proto p)
|
|||||||
case Proto::Cloak: return true;
|
case Proto::Cloak: return true;
|
||||||
case Proto::ShadowSocks: return true;
|
case Proto::ShadowSocks: return true;
|
||||||
case Proto::WireGuard: return true;
|
case Proto::WireGuard: return true;
|
||||||
|
case Proto::Awg: return true;
|
||||||
case Proto::Ikev2: return false;
|
case Proto::Ikev2: return false;
|
||||||
case Proto::L2tp: return false;
|
case Proto::L2tp: return false;
|
||||||
|
|
||||||
case Proto::TorWebSite: return true;
|
case Proto::TorWebSite: return false;
|
||||||
case Proto::Dns: return false;
|
case Proto::Dns: return false;
|
||||||
case Proto::FileShare: return false;
|
case Proto::Sftp: return true;
|
||||||
default: return -1;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,12 +159,12 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
|
|||||||
case Proto::Cloak: return TransportProto::Tcp;
|
case Proto::Cloak: return TransportProto::Tcp;
|
||||||
case Proto::ShadowSocks: return TransportProto::Tcp;
|
case Proto::ShadowSocks: return TransportProto::Tcp;
|
||||||
case Proto::WireGuard: return TransportProto::Udp;
|
case Proto::WireGuard: return TransportProto::Udp;
|
||||||
|
case Proto::Awg: return TransportProto::Udp;
|
||||||
case Proto::Ikev2: return TransportProto::Udp;
|
case Proto::Ikev2: return TransportProto::Udp;
|
||||||
case Proto::L2tp: return TransportProto::Udp;
|
case Proto::L2tp: return TransportProto::Udp;
|
||||||
// non-vpn
|
// non-vpn
|
||||||
case Proto::TorWebSite: return TransportProto::Tcp;
|
case Proto::TorWebSite: return TransportProto::Tcp;
|
||||||
case Proto::Dns: return TransportProto::Udp;
|
case Proto::Dns: return TransportProto::Udp;
|
||||||
case Proto::FileShare: return TransportProto::Udp;
|
|
||||||
case Proto::Sftp: return TransportProto::Tcp;
|
case Proto::Sftp: return TransportProto::Tcp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,12 +177,12 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
|
|||||||
case Proto::Cloak: return false;
|
case Proto::Cloak: return false;
|
||||||
case Proto::ShadowSocks: return false;
|
case Proto::ShadowSocks: return false;
|
||||||
case Proto::WireGuard: return false;
|
case Proto::WireGuard: return false;
|
||||||
|
case Proto::Awg: return false;
|
||||||
case Proto::Ikev2: return false;
|
case Proto::Ikev2: return false;
|
||||||
case Proto::L2tp: return false;
|
case Proto::L2tp: return false;
|
||||||
// non-vpn
|
// non-vpn
|
||||||
case Proto::TorWebSite: return false;
|
case Proto::TorWebSite: return false;
|
||||||
case Proto::Dns: return false;
|
case Proto::Dns: return false;
|
||||||
case Proto::FileShare: return false;
|
|
||||||
case Proto::Sftp: return false;
|
case Proto::Sftp: return false;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
#define PROTOCOLS_DEFS_H
|
#define PROTOCOLS_DEFS_H
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QObject>
|
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
@@ -43,6 +43,7 @@ namespace amnezia
|
|||||||
constexpr char server_priv_key[] = "server_priv_key";
|
constexpr char server_priv_key[] = "server_priv_key";
|
||||||
constexpr char server_pub_key[] = "server_pub_key";
|
constexpr char server_pub_key[] = "server_pub_key";
|
||||||
constexpr char psk_key[] = "psk_key";
|
constexpr char psk_key[] = "psk_key";
|
||||||
|
constexpr char allowed_ips[] = "allowed_ips";
|
||||||
|
|
||||||
constexpr char client_ip[] = "client_ip"; // internal ip address
|
constexpr char client_ip[] = "client_ip"; // internal ip address
|
||||||
|
|
||||||
@@ -61,11 +62,25 @@ namespace amnezia
|
|||||||
|
|
||||||
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
|
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
|
||||||
|
|
||||||
|
constexpr char junkPacketCount[] = "Jc";
|
||||||
|
constexpr char junkPacketMinSize[] = "Jmin";
|
||||||
|
constexpr char junkPacketMaxSize[] = "Jmax";
|
||||||
|
constexpr char initPacketJunkSize[] = "S1";
|
||||||
|
constexpr char responsePacketJunkSize[] = "S2";
|
||||||
|
constexpr char initPacketMagicHeader[] = "H1";
|
||||||
|
constexpr char responsePacketMagicHeader[] = "H2";
|
||||||
|
constexpr char underloadPacketMagicHeader[] = "H3";
|
||||||
|
constexpr char transportPacketMagicHeader[] = "H4";
|
||||||
|
|
||||||
constexpr char openvpn[] = "openvpn";
|
constexpr char openvpn[] = "openvpn";
|
||||||
constexpr char wireguard[] = "wireguard";
|
constexpr char wireguard[] = "wireguard";
|
||||||
constexpr char shadowsocks[] = "shadowsocks";
|
constexpr char shadowsocks[] = "shadowsocks";
|
||||||
constexpr char cloak[] = "cloak";
|
constexpr char cloak[] = "cloak";
|
||||||
constexpr char sftp[] = "sftp";
|
constexpr char sftp[] = "sftp";
|
||||||
|
constexpr char awg[] = "awg";
|
||||||
|
|
||||||
|
constexpr char splitTunnelSites[] = "splitTunnelSites";
|
||||||
|
constexpr char splitTunnelType[] = "splitTunnelType";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +155,25 @@ namespace amnezia
|
|||||||
|
|
||||||
} // namespace sftp
|
} // namespace sftp
|
||||||
|
|
||||||
|
namespace awg
|
||||||
|
{
|
||||||
|
constexpr char defaultPort[] = "55424";
|
||||||
|
|
||||||
|
constexpr char serverConfigPath[] = "/opt/amnezia/awg/wg0.conf";
|
||||||
|
constexpr char serverPublicKeyPath[] = "/opt/amnezia/awg/wireguard_server_public_key.key";
|
||||||
|
constexpr char serverPskKeyPath[] = "/opt/amnezia/awg/wireguard_psk.key";
|
||||||
|
|
||||||
|
constexpr char defaultJunkPacketCount[] = "3";
|
||||||
|
constexpr char defaultJunkPacketMinSize[] = "10";
|
||||||
|
constexpr char defaultJunkPacketMaxSize[] = "30";
|
||||||
|
constexpr char defaultInitPacketJunkSize[] = "15";
|
||||||
|
constexpr char defaultResponsePacketJunkSize[] = "18";
|
||||||
|
constexpr char defaultInitPacketMagicHeader[] = "1020325451";
|
||||||
|
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
|
||||||
|
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
|
||||||
|
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace protocols
|
} // namespace protocols
|
||||||
|
|
||||||
namespace ProtocolEnumNS
|
namespace ProtocolEnumNS
|
||||||
@@ -158,13 +192,13 @@ namespace amnezia
|
|||||||
ShadowSocks,
|
ShadowSocks,
|
||||||
Cloak,
|
Cloak,
|
||||||
WireGuard,
|
WireGuard,
|
||||||
|
Awg,
|
||||||
Ikev2,
|
Ikev2,
|
||||||
L2tp,
|
L2tp,
|
||||||
|
|
||||||
// non-vpn
|
// non-vpn
|
||||||
TorWebSite,
|
TorWebSite,
|
||||||
Dns,
|
Dns,
|
||||||
FileShare,
|
|
||||||
Sftp
|
Sftp
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(Proto)
|
Q_ENUM_NS(Proto)
|
||||||
@@ -198,6 +232,8 @@ namespace amnezia
|
|||||||
|
|
||||||
Q_INVOKABLE static ServiceType protocolService(Proto p);
|
Q_INVOKABLE static ServiceType protocolService(Proto p);
|
||||||
|
|
||||||
|
Q_INVOKABLE static int getPortForInstall(Proto p);
|
||||||
|
|
||||||
Q_INVOKABLE static int defaultPort(Proto p);
|
Q_INVOKABLE static int defaultPort(Proto p);
|
||||||
Q_INVOKABLE static bool defaultPortChangeable(Proto p);
|
Q_INVOKABLE static bool defaultPortChangeable(Proto p);
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "vpnprotocol.h"
|
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
|
#include "vpnprotocol.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
#include "openvpnprotocol.h"
|
#include "openvpnovercloakprotocol.h"
|
||||||
#include "shadowsocksvpnprotocol.h"
|
#include "openvpnprotocol.h"
|
||||||
#include "openvpnovercloakprotocol.h"
|
#include "shadowsocksvpnprotocol.h"
|
||||||
#include "wireguardprotocol.h"
|
#include "wireguardprotocol.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
#include "ikev2_vpn_protocol_windows.h"
|
#include "ikev2_vpn_protocol_windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject *parent)
|
||||||
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject* parent)
|
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_connectionState(Vpn::ConnectionState::Unknown),
|
m_connectionState(Vpn::ConnectionState::Unknown),
|
||||||
m_rawConfig(configuration),
|
m_rawConfig(configuration),
|
||||||
@@ -31,7 +30,7 @@ VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject* parent)
|
|||||||
void VpnProtocol::setLastError(ErrorCode lastError)
|
void VpnProtocol::setLastError(ErrorCode lastError)
|
||||||
{
|
{
|
||||||
m_lastError = lastError;
|
m_lastError = lastError;
|
||||||
if (lastError){
|
if (lastError) {
|
||||||
setConnectionState(Vpn::ConnectionState::Error);
|
setConnectionState(Vpn::ConnectionState::Error);
|
||||||
}
|
}
|
||||||
qCritical().noquote() << "VpnProtocol error, code" << m_lastError << errorString(m_lastError);
|
qCritical().noquote() << "VpnProtocol error, code" << m_lastError << errorString(m_lastError);
|
||||||
@@ -103,7 +102,7 @@ QString VpnProtocol::vpnGateway() const
|
|||||||
return m_vpnGateway;
|
return m_vpnGateway;
|
||||||
}
|
}
|
||||||
|
|
||||||
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject& configuration)
|
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
#if defined(Q_OS_WINDOWS)
|
#if defined(Q_OS_WINDOWS)
|
||||||
@@ -114,6 +113,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject&
|
|||||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||||
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
|
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
|
||||||
|
case DockerContainer::Awg: return new WireguardProtocol(configuration);
|
||||||
#endif
|
#endif
|
||||||
default: return nullptr;
|
default: return nullptr;
|
||||||
}
|
}
|
||||||
@@ -135,8 +135,7 @@ QString VpnProtocol::textConnectionState(Vpn::ConnectionState connectionState)
|
|||||||
case Vpn::ConnectionState::Disconnecting: return tr("Disconnecting...");
|
case Vpn::ConnectionState::Disconnecting: return tr("Disconnecting...");
|
||||||
case Vpn::ConnectionState::Reconnecting: return tr("Reconnecting...");
|
case Vpn::ConnectionState::Reconnecting: return tr("Reconnecting...");
|
||||||
case Vpn::ConnectionState::Error: return tr("Error");
|
case Vpn::ConnectionState::Error: return tr("Error");
|
||||||
default:
|
default:;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
|
|||||||
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
|
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
|
||||||
writeWireguardConfiguration(configuration);
|
writeWireguardConfiguration(configuration);
|
||||||
|
|
||||||
// MZ
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
m_impl.reset(new LocalSocketController());
|
m_impl.reset(new LocalSocketController());
|
||||||
connect(m_impl.get(), &ControllerImpl::connected, this,
|
connect(m_impl.get(), &ControllerImpl::connected, this,
|
||||||
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
|
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
|
||||||
@@ -26,7 +24,6 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
|
|||||||
connect(m_impl.get(), &ControllerImpl::disconnected, this,
|
connect(m_impl.get(), &ControllerImpl::disconnected, this,
|
||||||
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
|
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
|
||||||
m_impl->initialize(nullptr, nullptr);
|
m_impl->initialize(nullptr, nullptr);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WireguardProtocol::~WireguardProtocol()
|
WireguardProtocol::~WireguardProtocol()
|
||||||
@@ -37,72 +34,12 @@ WireguardProtocol::~WireguardProtocol()
|
|||||||
|
|
||||||
void WireguardProtocol::stop()
|
void WireguardProtocol::stop()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
stopMzImpl();
|
stopMzImpl();
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
|
|
||||||
qCritical() << "Wireguard executable missing!";
|
|
||||||
setLastError(ErrorCode::ExecutableMissing);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wireguardStopProcess = IpcClient::CreatePrivilegedProcess();
|
|
||||||
|
|
||||||
if (!m_wireguardStopProcess) {
|
|
||||||
qCritical() << "IpcProcess replica is not created!";
|
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wireguardStopProcess->waitForSource(1000);
|
|
||||||
if (!m_wireguardStopProcess->isInitialized()) {
|
|
||||||
qWarning() << "IpcProcess replica is not connected!";
|
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard);
|
|
||||||
|
|
||||||
m_wireguardStopProcess->setArguments(stopArgs());
|
|
||||||
qDebug() << stopArgs().join(" ");
|
|
||||||
|
|
||||||
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this,
|
|
||||||
[this](QProcess::ProcessState newState) {
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
|
|
||||||
});
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
if (IpcClient::Interface()) {
|
|
||||||
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
|
|
||||||
if (result.returnValue()) {
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qCritical() << "IPC client not initialized";
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_wireguardStopProcess->start();
|
|
||||||
m_wireguardStopProcess->waitForFinished(10000);
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
ErrorCode WireguardProtocol::startMzImpl()
|
ErrorCode WireguardProtocol::startMzImpl()
|
||||||
{
|
{
|
||||||
|
|
||||||
qDebug() << "WireguardProtocol::startMzImpl():" << m_rawConfig;
|
|
||||||
m_impl->activate(m_rawConfig);
|
m_impl->activate(m_rawConfig);
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
@@ -112,7 +49,6 @@ ErrorCode WireguardProtocol::stopMzImpl()
|
|||||||
m_impl->deactivate();
|
m_impl->deactivate();
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
|
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
|
||||||
{
|
{
|
||||||
@@ -126,21 +62,8 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
|
|||||||
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
|
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
|
||||||
m_configFile.close();
|
m_configFile.close();
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (IpcClient::Interface()) {
|
|
||||||
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName());
|
|
||||||
if (result.returnValue()) {
|
|
||||||
qCritical() << "Failed to copy wireguard config";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qCritical() << "IPC client not initialized";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_configFileName = "/etc/wireguard/wg99.conf";
|
|
||||||
#else
|
|
||||||
m_configFileName = m_configFile.fileName();
|
m_configFileName = m_configFile.fileName();
|
||||||
#endif
|
|
||||||
|
|
||||||
m_isConfigLoaded = true;
|
m_isConfigLoaded = true;
|
||||||
|
|
||||||
@@ -154,15 +77,9 @@ QString WireguardProtocol::configPath() const
|
|||||||
return m_configFileName;
|
return m_configFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WireguardProtocol::updateRouteGateway(QString line)
|
QString WireguardProtocol::serviceName() const
|
||||||
{
|
{
|
||||||
// TODO: fix for macos
|
return "AmneziaVPN.WireGuard0";
|
||||||
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
|
|
||||||
if (!line.contains("/"))
|
|
||||||
return;
|
|
||||||
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
|
|
||||||
m_routeGateway.replace(" ", "");
|
|
||||||
qDebug() << "Set VPN route gateway" << m_routeGateway;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode WireguardProtocol::start()
|
ErrorCode WireguardProtocol::start()
|
||||||
@@ -172,112 +89,6 @@ ErrorCode WireguardProtocol::start()
|
|||||||
return lastError();
|
return lastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
return startMzImpl();
|
return startMzImpl();
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
|
|
||||||
setLastError(ErrorCode::ExecutableMissing);
|
|
||||||
return lastError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IpcClient::Interface()) {
|
|
||||||
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardConfigExists(configPath());
|
|
||||||
if (result.returnValue()) {
|
|
||||||
setLastError(ErrorCode::ConfigMissing);
|
|
||||||
return lastError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qCritical() << "IPC client not initialized";
|
|
||||||
setLastError(ErrorCode::InternalError);
|
|
||||||
return lastError();
|
|
||||||
}
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
|
||||||
|
|
||||||
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
|
|
||||||
|
|
||||||
if (!m_wireguardStartProcess) {
|
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wireguardStartProcess->waitForSource(1000);
|
|
||||||
if (!m_wireguardStartProcess->isInitialized()) {
|
|
||||||
qWarning() << "IpcProcess replica is not connected!";
|
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard);
|
|
||||||
|
|
||||||
m_wireguardStartProcess->setArguments(startArgs());
|
|
||||||
qDebug() << startArgs().join(" ");
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this,
|
|
||||||
[this](QProcess::ProcessState newState) {
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this,
|
|
||||||
[this]() { setConnectionState(Vpn::ConnectionState::Connected); });
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
|
|
||||||
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAll();
|
|
||||||
reply.waitForFinished(1000);
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol readyRead" << reply.returnValue();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardOutput, this, [this]() {
|
|
||||||
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardOutput();
|
|
||||||
reply.waitForFinished(1000);
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardOutput" << reply.returnValue();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardError, this, [this]() {
|
|
||||||
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardError();
|
|
||||||
reply.waitForFinished(10);
|
|
||||||
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_wireguardStartProcess->start();
|
|
||||||
m_wireguardStartProcess->waitForFinished(10000);
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WireguardProtocol::updateVpnGateway(const QString &line)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString WireguardProtocol::serviceName() const
|
|
||||||
{
|
|
||||||
return "AmneziaVPN.WireGuard0";
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList WireguardProtocol::stopArgs()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
return { "--remove", configPath() };
|
|
||||||
#elif defined Q_OS_LINUX
|
|
||||||
return { "down", "wg99" };
|
|
||||||
#else
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList WireguardProtocol::startArgs()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
return { "--add", configPath() };
|
|
||||||
#elif defined Q_OS_LINUX
|
|
||||||
return { "up", "wg99" };
|
|
||||||
#else
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
#include "core/ipcclient.h"
|
|
||||||
|
|
||||||
#include "mozilla/controllerimpl.h"
|
#include "mozilla/controllerimpl.h"
|
||||||
|
|
||||||
@@ -23,33 +22,21 @@ public:
|
|||||||
ErrorCode start() override;
|
ErrorCode start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
ErrorCode startMzImpl();
|
ErrorCode startMzImpl();
|
||||||
ErrorCode stopMzImpl();
|
ErrorCode stopMzImpl();
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString configPath() const;
|
QString configPath() const;
|
||||||
void writeWireguardConfiguration(const QJsonObject &configuration);
|
void writeWireguardConfiguration(const QJsonObject &configuration);
|
||||||
|
|
||||||
void updateRouteGateway(QString line);
|
|
||||||
void updateVpnGateway(const QString &line);
|
|
||||||
QString serviceName() const;
|
QString serviceName() const;
|
||||||
QStringList stopArgs();
|
|
||||||
QStringList startArgs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_configFileName;
|
QString m_configFileName;
|
||||||
QFile m_configFile;
|
QFile m_configFile;
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> m_wireguardStartProcess;
|
|
||||||
QSharedPointer<PrivilegedProcess> m_wireguardStopProcess;
|
|
||||||
|
|
||||||
bool m_isConfigLoaded = false;
|
bool m_isConfigLoaded = false;
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
|
||||||
QScopedPointer<ControllerImpl> m_impl;
|
QScopedPointer<ControllerImpl> m_impl;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARDPROTOCOL_H
|
#endif // WIREGUARDPROTOCOL_H
|
||||||
|
|||||||
@@ -215,5 +215,12 @@
|
|||||||
<file>ui/qml/Controls2/ListViewWithLabelsType.qml</file>
|
<file>ui/qml/Controls2/ListViewWithLabelsType.qml</file>
|
||||||
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
|
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
|
||||||
<file>ui/qml/Controls2/TopCloseButtonType.qml</file>
|
<file>ui/qml/Controls2/TopCloseButtonType.qml</file>
|
||||||
|
<file>images/controls/x-circle.svg</file>
|
||||||
|
<file>ui/qml/Pages2/PageProtocolAwgSettings.qml</file>
|
||||||
|
<file>server_scripts/awg/template.conf</file>
|
||||||
|
<file>server_scripts/awg/start.sh</file>
|
||||||
|
<file>server_scripts/awg/configure_container.sh</file>
|
||||||
|
<file>server_scripts/awg/run_container.sh</file>
|
||||||
|
<file>server_scripts/awg/Dockerfile</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
FROM amneziavpn/amnezia-wg:latest
|
||||||
|
|
||||||
|
LABEL maintainer="AmneziaVPN"
|
||||||
|
|
||||||
|
#Install required packages
|
||||||
|
RUN apk add --no-cache bash curl dumb-init
|
||||||
|
RUN apk --update upgrade --no-cache
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/amnezia
|
||||||
|
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
|
||||||
|
RUN chmod a+x /opt/amnezia/start.sh
|
||||||
|
|
||||||
|
# Tune network
|
||||||
|
RUN echo -e " \n\
|
||||||
|
fs.file-max = 51200 \n\
|
||||||
|
\n\
|
||||||
|
net.core.rmem_max = 67108864 \n\
|
||||||
|
net.core.wmem_max = 67108864 \n\
|
||||||
|
net.core.netdev_max_backlog = 250000 \n\
|
||||||
|
net.core.somaxconn = 4096 \n\
|
||||||
|
\n\
|
||||||
|
net.ipv4.tcp_syncookies = 1 \n\
|
||||||
|
net.ipv4.tcp_tw_reuse = 1 \n\
|
||||||
|
net.ipv4.tcp_tw_recycle = 0 \n\
|
||||||
|
net.ipv4.tcp_fin_timeout = 30 \n\
|
||||||
|
net.ipv4.tcp_keepalive_time = 1200 \n\
|
||||||
|
net.ipv4.ip_local_port_range = 10000 65000 \n\
|
||||||
|
net.ipv4.tcp_max_syn_backlog = 8192 \n\
|
||||||
|
net.ipv4.tcp_max_tw_buckets = 5000 \n\
|
||||||
|
net.ipv4.tcp_fastopen = 3 \n\
|
||||||
|
net.ipv4.tcp_mem = 25600 51200 102400 \n\
|
||||||
|
net.ipv4.tcp_rmem = 4096 87380 67108864 \n\
|
||||||
|
net.ipv4.tcp_wmem = 4096 65536 67108864 \n\
|
||||||
|
net.ipv4.tcp_mtu_probing = 1 \n\
|
||||||
|
net.ipv4.tcp_congestion_control = hybla \n\
|
||||||
|
# for low-latency network, use cubic instead \n\
|
||||||
|
# net.ipv4.tcp_congestion_control = cubic \n\
|
||||||
|
" | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \
|
||||||
|
mkdir -p /etc/security && \
|
||||||
|
echo -e " \n\
|
||||||
|
* soft nofile 51200 \n\
|
||||||
|
* hard nofile 51200 \n\
|
||||||
|
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
|
||||||
|
|
||||||
|
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
|
||||||
|
CMD [ "" ]
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
mkdir -p /opt/amnezia/awg
|
||||||
|
cd /opt/amnezia/awg
|
||||||
|
WIREGUARD_SERVER_PRIVATE_KEY=$(wg genkey)
|
||||||
|
echo $WIREGUARD_SERVER_PRIVATE_KEY > /opt/amnezia/awg/wireguard_server_private_key.key
|
||||||
|
|
||||||
|
WIREGUARD_SERVER_PUBLIC_KEY=$(echo $WIREGUARD_SERVER_PRIVATE_KEY | wg pubkey)
|
||||||
|
echo $WIREGUARD_SERVER_PUBLIC_KEY > /opt/amnezia/awg/wireguard_server_public_key.key
|
||||||
|
|
||||||
|
WIREGUARD_PSK=$(wg genpsk)
|
||||||
|
echo $WIREGUARD_PSK > /opt/amnezia/awg/wireguard_psk.key
|
||||||
|
|
||||||
|
cat > /opt/amnezia/awg/wg0.conf <<EOF
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = $WIREGUARD_SERVER_PRIVATE_KEY
|
||||||
|
Address = $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR
|
||||||
|
ListenPort = $AWG_SERVER_PORT
|
||||||
|
Jc = $JUNK_PACKET_COUNT
|
||||||
|
Jmin = $JUNK_PACKET_MIN_SIZE
|
||||||
|
Jmax = $JUNK_PACKET_MAX_SIZE
|
||||||
|
S1 = $INIT_PACKET_JUNK_SIZE
|
||||||
|
S2 = $RESPONSE_PACKET_JUNK_SIZE
|
||||||
|
H1 = $INIT_PACKET_MAGIC_HEADER
|
||||||
|
H2 = $RESPONSE_PACKET_MAGIC_HEADER
|
||||||
|
H3 = $UNDERLOAD_PACKET_MAGIC_HEADER
|
||||||
|
H4 = $TRANSPORT_PACKET_MAGIC_HEADER
|
||||||
|
EOF
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Run container
|
||||||
|
sudo docker run -d \
|
||||||
|
--log-driver none \
|
||||||
|
--restart always \
|
||||||
|
--privileged \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
--cap-add=SYS_MODULE \
|
||||||
|
-p $AWG_SERVER_PORT:$AWG_SERVER_PORT/udp \
|
||||||
|
-v /lib/modules:/lib/modules \
|
||||||
|
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
|
||||||
|
--name $CONTAINER_NAME \
|
||||||
|
$CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
|
# Prevent to route packets outside of the container in case if server behind of the NAT
|
||||||
|
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
|
||||||
|
|
||||||
|
echo "Container startup"
|
||||||
|
#ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up
|
||||||
|
|
||||||
|
# kill daemons in case of restart
|
||||||
|
wg-quick down /opt/amnezia/awg/wg0.conf
|
||||||
|
|
||||||
|
# start daemons if configured
|
||||||
|
if [ -f /opt/amnezia/awg/wg0.conf ]; then (wg-quick up /opt/amnezia/awg/wg0.conf); fi
|
||||||
|
|
||||||
|
# Allow traffic on the TUN interface.
|
||||||
|
iptables -A INPUT -i wg0 -j ACCEPT
|
||||||
|
iptables -A FORWARD -i wg0 -j ACCEPT
|
||||||
|
iptables -A OUTPUT -o wg0 -j ACCEPT
|
||||||
|
|
||||||
|
# Allow forwarding traffic only from the VPN.
|
||||||
|
iptables -A FORWARD -i wg0 -o eth0 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -j ACCEPT
|
||||||
|
iptables -A FORWARD -i wg0 -o eth1 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -j ACCEPT
|
||||||
|
|
||||||
|
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
|
iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -o eth0 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -o eth1 -j MASQUERADE
|
||||||
|
|
||||||
|
tail -f /dev/null
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[Interface]
|
||||||
|
Address = $WIREGUARD_CLIENT_IP/32
|
||||||
|
DNS = $PRIMARY_DNS, $SECONDARY_DNS
|
||||||
|
PrivateKey = $WIREGUARD_CLIENT_PRIVATE_KEY
|
||||||
|
Jc = $JUNK_PACKET_COUNT
|
||||||
|
Jmin = $JUNK_PACKET_MIN_SIZE
|
||||||
|
Jmax = $JUNK_PACKET_MAX_SIZE
|
||||||
|
S1 = $INIT_PACKET_JUNK_SIZE
|
||||||
|
S2 = $RESPONSE_PACKET_JUNK_SIZE
|
||||||
|
H1 = $INIT_PACKET_MAGIC_HEADER
|
||||||
|
H2 = $RESPONSE_PACKET_MAGIC_HEADER
|
||||||
|
H3 = $UNDERLOAD_PACKET_MAGIC_HEADER
|
||||||
|
H4 = $TRANSPORT_PACKET_MAGIC_HEADER
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = $WIREGUARD_SERVER_PUBLIC_KEY
|
||||||
|
PresharedKey = $WIREGUARD_PSK
|
||||||
|
AllowedIPs = 0.0.0.0/0, ::/0
|
||||||
|
Endpoint = $SERVER_IP_ADDRESS:$AWG_SERVER_PORT
|
||||||
|
PersistentKeepalive = 25
|
||||||
@@ -1 +1 @@
|
|||||||
sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m)
|
sudo docker build --no-cache --pull -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m)
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); docker_pkg="docker.io"; dist="debian";\
|
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); docker_pkg="docker"; dist="fedora";\
|
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); docker_pkg="docker"; dist="centos";\
|
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";\
|
||||||
else echo "Packet manager not found"; exit 1; fi;\
|
else echo "Packet manager not found"; exit 1; fi;\
|
||||||
echo "Dist: $dist, Packet manager: $pm, Docker pkg: $docker_pkg";\
|
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
|
||||||
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
|
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then $pm update -yq; $pm install -yq sudo; fi;\
|
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
|
||||||
if ! command -v fuser > /dev/null 2>&1; then $pm install -yq psmisc; fi;\
|
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
|
||||||
if ! command -v lsof > /dev/null 2>&1; then $pm install -yq lsof; fi;\
|
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
|
||||||
if ! command -v docker > /dev/null 2>&1; then $pm update -yq; $pm install -yq $docker_pkg;\
|
if ! command -v docker > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
||||||
if [ "$dist" = "fedora" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
|
if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
|
||||||
fi;\
|
fi;\
|
||||||
if [ "$dist" = "debian" ]; then \
|
if [ "$dist" = "debian" ]; then \
|
||||||
docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\
|
docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\
|
||||||
if [ -z "$docker_service" ]; then sudo $pm update -yq; sudo $pm install -yq curl $docker_pkg; fi;\
|
if [ -z "$docker_service" ]; then sudo $pm $check_pkgs; sudo $pm $silent_inst curl $docker_pkg; fi;\
|
||||||
sleep 3 && sudo systemctl start docker && sleep 3;\
|
sleep 3 && sudo systemctl start docker && sleep 3;\
|
||||||
fi;\
|
fi;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker";exit 1;fi;\
|
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker"; exit 1; fi;\
|
||||||
docker --version
|
docker --version
|
||||||
|
|
||||||
|
|||||||
@@ -233,10 +233,6 @@ QString Settings::routeModeString(RouteMode mode) const
|
|||||||
|
|
||||||
Settings::RouteMode Settings::routeMode() const
|
Settings::RouteMode Settings::routeMode() const
|
||||||
{
|
{
|
||||||
// TODO implement for mobiles
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
|
||||||
return RouteMode::VpnAllSites;
|
|
||||||
#endif
|
|
||||||
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
|
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1780
-2372
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/translations">
|
||||||
|
@QM_FILE_LIST@
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -19,23 +19,8 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn,
|
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionController::~ConnectionController()
|
m_state = Vpn::ConnectionState::Disconnected;
|
||||||
{
|
|
||||||
// todo use ConnectionController instead of using m_vpnConnection directly
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
if (m_vpnConnection->connectionState() != Vpn::ConnectionState::Disconnected) {
|
|
||||||
m_vpnConnection->disconnectFromVpn();
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
|
||||||
QThread::msleep(100);
|
|
||||||
if (m_vpnConnection->isDisconnected()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
@@ -70,6 +55,8 @@ QString ConnectionController::getLastConnectionError()
|
|||||||
|
|
||||||
void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
|
void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||||
{
|
{
|
||||||
|
m_state = state;
|
||||||
|
|
||||||
m_isConnected = false;
|
m_isConnected = false;
|
||||||
m_connectionStateText = tr("Connection...");
|
m_connectionStateText = tr("Connection...");
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@@ -126,6 +113,17 @@ void ConnectionController::onCurrentContainerUpdated()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionController::onTranslationsUpdated()
|
||||||
|
{
|
||||||
|
// get translated text of current state
|
||||||
|
onConnectionStateChanged(getCurrentConnectionState());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vpn::ConnectionState ConnectionController::getCurrentConnectionState()
|
||||||
|
{
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
QString ConnectionController::connectionStateText() const
|
QString ConnectionController::connectionStateText() const
|
||||||
{
|
{
|
||||||
return m_connectionStateText;
|
return m_connectionStateText;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
const QSharedPointer<ContainersModel> &containersModel,
|
const QSharedPointer<ContainersModel> &containersModel,
|
||||||
const QSharedPointer<VpnConnection> &vpnConnection, QObject *parent = nullptr);
|
const QSharedPointer<VpnConnection> &vpnConnection, QObject *parent = nullptr);
|
||||||
|
|
||||||
~ConnectionController();
|
~ConnectionController() = default;
|
||||||
|
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
bool isConnectionInProgress() const;
|
bool isConnectionInProgress() const;
|
||||||
@@ -34,6 +34,8 @@ public slots:
|
|||||||
|
|
||||||
void onCurrentContainerUpdated();
|
void onCurrentContainerUpdated();
|
||||||
|
|
||||||
|
void onTranslationsUpdated();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig);
|
const QJsonObject &containerConfig);
|
||||||
@@ -44,6 +46,8 @@ signals:
|
|||||||
void reconnectWithUpdatedContainer(const QString &message);
|
void reconnectWithUpdatedContainer(const QString &message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Vpn::ConnectionState getCurrentConnectionState();
|
||||||
|
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
|
|
||||||
@@ -52,6 +56,8 @@ private:
|
|||||||
bool m_isConnected = false;
|
bool m_isConnected = false;
|
||||||
bool m_isConnectionInProgress = false;
|
bool m_isConnectionInProgress = false;
|
||||||
QString m_connectionStateText = tr("Connect");
|
QString m_connectionStateText = tr("Connect");
|
||||||
|
|
||||||
|
Vpn::ConnectionState m_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONNECTIONCONTROLLER_H
|
#endif // CONNECTIONCONTROLLER_H
|
||||||
|
|||||||
@@ -144,8 +144,6 @@ void ImportController::importConfig()
|
|||||||
if (credentials.isValid() || m_config.contains(config_key::containers)) {
|
if (credentials.isValid() || m_config.contains(config_key::containers)) {
|
||||||
m_serversModel->addServer(m_config);
|
m_serversModel->addServer(m_config);
|
||||||
|
|
||||||
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
|
|
||||||
|
|
||||||
emit importFinished();
|
emit importFinished();
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Failed to import profile";
|
qDebug() << "Failed to import profile";
|
||||||
@@ -214,21 +212,79 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
|||||||
|
|
||||||
QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
||||||
{
|
{
|
||||||
|
QMap<QString, QString> configMap;
|
||||||
|
auto configByLines = data.split("\n");
|
||||||
|
for (const QString &line : configByLines) {
|
||||||
|
QString trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
configMap[parts.at(0).trimmed()] = parts.at(1).trimmed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject lastConfig;
|
QJsonObject lastConfig;
|
||||||
lastConfig[config_key::config] = data;
|
lastConfig[config_key::config] = data;
|
||||||
|
|
||||||
const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*)(?::([0-9]*))?");
|
const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*):([0-9]*)");
|
||||||
QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(data);
|
QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(data);
|
||||||
QString hostName;
|
QString hostName;
|
||||||
QString port;
|
QString port;
|
||||||
if (hostNameAndPortMatch.hasCaptured(1)) {
|
if (hostNameAndPortMatch.hasCaptured(1)) {
|
||||||
hostName = hostNameAndPortMatch.captured(1);
|
hostName = hostNameAndPortMatch.captured(1);
|
||||||
} /*else {
|
} else {
|
||||||
qDebug() << "send error?"
|
qDebug() << "Failed to import profile";
|
||||||
}*/
|
emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError));
|
||||||
|
}
|
||||||
|
|
||||||
if (hostNameAndPortMatch.hasCaptured(2)) {
|
if (hostNameAndPortMatch.hasCaptured(2)) {
|
||||||
port = hostNameAndPortMatch.captured(2);
|
port = hostNameAndPortMatch.captured(2);
|
||||||
|
} else {
|
||||||
|
port = protocols::wireguard::defaultPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastConfig[config_key::hostName] = hostName;
|
||||||
|
lastConfig[config_key::port] = port.toInt();
|
||||||
|
|
||||||
|
// if (!configMap.value("PrivateKey").isEmpty() && !configMap.value("Address").isEmpty()
|
||||||
|
// && !configMap.value("PresharedKey").isEmpty() && !configMap.value("PublicKey").isEmpty()) {
|
||||||
|
lastConfig[config_key::client_priv_key] = configMap.value("PrivateKey");
|
||||||
|
lastConfig[config_key::client_ip] = configMap.value("Address");
|
||||||
|
lastConfig[config_key::psk_key] = configMap.value("PresharedKey");
|
||||||
|
lastConfig[config_key::server_pub_key] = configMap.value("PublicKey");
|
||||||
|
// } else {
|
||||||
|
// qDebug() << "Failed to import profile";
|
||||||
|
// emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError));
|
||||||
|
// return QJsonObject();
|
||||||
|
// }
|
||||||
|
|
||||||
|
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(","));
|
||||||
|
|
||||||
|
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);
|
||||||
|
protocolName = "awg";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject wireguardConfig;
|
QJsonObject wireguardConfig;
|
||||||
@@ -238,15 +294,15 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
|||||||
wireguardConfig[config_key::transport_proto] = "udp";
|
wireguardConfig[config_key::transport_proto] = "udp";
|
||||||
|
|
||||||
QJsonObject containers;
|
QJsonObject containers;
|
||||||
containers.insert(config_key::container, QJsonValue("amnezia-wireguard"));
|
containers.insert(config_key::container, QJsonValue("amnezia-" + protocolName));
|
||||||
containers.insert(config_key::wireguard, QJsonValue(wireguardConfig));
|
containers.insert(protocolName, QJsonValue(wireguardConfig));
|
||||||
|
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
arr.push_back(containers);
|
arr.push_back(containers);
|
||||||
|
|
||||||
QJsonObject config;
|
QJsonObject config;
|
||||||
config[config_key::containers] = arr;
|
config[config_key::containers] = arr;
|
||||||
config[config_key::defaultContainer] = "amnezia-wireguard";
|
config[config_key::defaultContainer] = "amnezia-" + protocolName;
|
||||||
config[config_key::description] = m_settings->nextAvailableServerName();
|
config[config_key::description] = m_settings->nextAvailableServerName();
|
||||||
|
|
||||||
const static QRegularExpression dnsRegExp(
|
const static QRegularExpression dnsRegExp(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/servercontroller.h"
|
||||||
@@ -73,6 +74,38 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
|||||||
containerConfig.insert(config_key::transport_proto,
|
containerConfig.insert(config_key::transport_proto,
|
||||||
ProtocolProps::transportProtoToString(transportProto, protocol));
|
ProtocolProps::transportProtoToString(transportProto, protocol));
|
||||||
|
|
||||||
|
if (container == DockerContainer::Awg) {
|
||||||
|
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(3, 10));
|
||||||
|
QString junkPacketMinSize = QString::number(50);
|
||||||
|
QString junkPacketMaxSize = QString::number(1000);
|
||||||
|
QString initPacketJunkSize = QString::number(QRandomGenerator::global()->bounded(15, 150));
|
||||||
|
QString responsePacketJunkSize = QString::number(QRandomGenerator::global()->bounded(15, 150));
|
||||||
|
|
||||||
|
QSet<QString> headersValue;
|
||||||
|
while (headersValue.size() != 4) {
|
||||||
|
|
||||||
|
auto max = (std::numeric_limits<qint32>::max)();
|
||||||
|
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(1, max)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto headersValueList = headersValue.values();
|
||||||
|
|
||||||
|
QString initPacketMagicHeader = headersValueList.at(0);
|
||||||
|
QString responsePacketMagicHeader = headersValueList.at(1);
|
||||||
|
QString underloadPacketMagicHeader = headersValueList.at(2);
|
||||||
|
QString transportPacketMagicHeader = headersValueList.at(3);
|
||||||
|
|
||||||
|
containerConfig[config_key::junkPacketCount] = junkPacketCount;
|
||||||
|
containerConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
|
||||||
|
containerConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
|
||||||
|
containerConfig[config_key::initPacketJunkSize] = initPacketJunkSize;
|
||||||
|
containerConfig[config_key::responsePacketJunkSize] = responsePacketJunkSize;
|
||||||
|
containerConfig[config_key::initPacketMagicHeader] = initPacketMagicHeader;
|
||||||
|
containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader;
|
||||||
|
containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader;
|
||||||
|
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
|
||||||
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::Sftp) {
|
if (container == DockerContainer::Sftp) {
|
||||||
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||||
containerConfig.insert(config_key::password, Utils::getRandomString(10));
|
containerConfig.insert(config_key::password, Utils::getRandomString(10));
|
||||||
@@ -107,10 +140,9 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
|
|||||||
if (!installedContainers.contains(container)) {
|
if (!installedContainers.contains(container)) {
|
||||||
errorCode = serverController.setupContainer(m_currentlyInstalledServerCredentials, container, config);
|
errorCode = serverController.setupContainer(m_currentlyInstalledServerCredentials, container, config);
|
||||||
installedContainers.insert(container, config);
|
installedContainers.insert(container, config);
|
||||||
finishMessage = ContainerProps::containerHumanNames().value(container) + tr(" installed successfully. ");
|
finishMessage = tr("%1 installed successfully. ").arg(ContainerProps::containerHumanNames().value(container));
|
||||||
} else {
|
} else {
|
||||||
finishMessage =
|
finishMessage = tr("%1 is already installed on the server. ").arg(ContainerProps::containerHumanNames().value(container));
|
||||||
ContainerProps::containerHumanNames().value(container) + tr(" is already installed on the server. ");
|
|
||||||
}
|
}
|
||||||
if (installedContainers.size() > 1) {
|
if (installedContainers.size() > 1) {
|
||||||
finishMessage += tr("\nAdded containers that were already installed on the server");
|
finishMessage += tr("\nAdded containers that were already installed on the server");
|
||||||
@@ -133,7 +165,6 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
|
|||||||
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
|
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
m_serversModel->addServer(server);
|
m_serversModel->addServer(server);
|
||||||
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
|
|
||||||
|
|
||||||
emit installServerFinished(finishMessage);
|
emit installServerFinished(finishMessage);
|
||||||
return;
|
return;
|
||||||
@@ -159,10 +190,9 @@ void InstallController::installContainer(DockerContainer container, QJsonObject
|
|||||||
if (!installedContainers.contains(container)) {
|
if (!installedContainers.contains(container)) {
|
||||||
errorCode = serverController.setupContainer(serverCredentials, container, config);
|
errorCode = serverController.setupContainer(serverCredentials, container, config);
|
||||||
installedContainers.insert(container, config);
|
installedContainers.insert(container, config);
|
||||||
finishMessage = ContainerProps::containerHumanNames().value(container) + tr(" installed successfully. ");
|
finishMessage = tr("%1 installed successfully. ").arg(ContainerProps::containerHumanNames().value(container));
|
||||||
} else {
|
} else {
|
||||||
finishMessage =
|
finishMessage = tr("%1 is already installed on the server. ").arg(ContainerProps::containerHumanNames().value(container));
|
||||||
ContainerProps::containerHumanNames().value(container) + tr(" is already installed on the server. ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInstalledContainerAddedToGui = false;
|
bool isInstalledContainerAddedToGui = false;
|
||||||
@@ -277,7 +307,7 @@ void InstallController::removeCurrentlyProcessedServer()
|
|||||||
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
||||||
|
|
||||||
m_serversModel->removeServer();
|
m_serversModel->removeServer();
|
||||||
emit removeCurrentlyProcessedServerFinished(tr("Server '") + serverName + tr("' was removed"));
|
emit removeCurrentlyProcessedServerFinished(tr("Server '%1' was removed").arg(serverName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallController::removeAllContainers()
|
void InstallController::removeAllContainers()
|
||||||
@@ -287,7 +317,7 @@ void InstallController::removeAllContainers()
|
|||||||
|
|
||||||
ErrorCode errorCode = m_containersModel->removeAllContainers();
|
ErrorCode errorCode = m_containersModel->removeAllContainers();
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
emit removeAllContainersFinished(tr("All containers from server '") + serverName + ("' have been removed"));
|
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit installationErrorOccurred(errorString(errorCode));
|
emit installationErrorOccurred(errorString(errorCode));
|
||||||
@@ -303,8 +333,8 @@ void InstallController::removeCurrentlyProcessedContainer()
|
|||||||
|
|
||||||
ErrorCode errorCode = m_containersModel->removeCurrentlyProcessedContainer();
|
ErrorCode errorCode = m_containersModel->removeCurrentlyProcessedContainer();
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
emit removeCurrentlyProcessedContainerFinished(containerName + tr(" has been removed from the server '")
|
|
||||||
+ serverName + "'");
|
emit removeCurrentlyProcessedContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName).arg(serverName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit installationErrorOccurred(errorString(errorCode));
|
emit installationErrorOccurred(errorString(errorCode));
|
||||||
@@ -474,8 +504,9 @@ void InstallController::addEmptyServer()
|
|||||||
server.insert(config_key::port, m_currentlyInstalledServerCredentials.port);
|
server.insert(config_key::port, m_currentlyInstalledServerCredentials.port);
|
||||||
server.insert(config_key::description, m_settings->nextAvailableServerName());
|
server.insert(config_key::description, m_settings->nextAvailableServerName());
|
||||||
|
|
||||||
|
server.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
|
||||||
|
|
||||||
m_serversModel->addServer(server);
|
m_serversModel->addServer(server);
|
||||||
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
|
|
||||||
|
|
||||||
emit installServerFinished(tr("Server added successfully"));
|
emit installServerFinished(tr("Server added successfully"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,3 +157,8 @@ void PageController::setTriggeredBtConnectButton(bool trigger)
|
|||||||
{
|
{
|
||||||
m_isTriggeredByConnectButton = trigger;
|
m_isTriggeredByConnectButton = trigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PageController::closeApplication()
|
||||||
|
{
|
||||||
|
qApp->quit();
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ namespace PageLoader
|
|||||||
PageProtocolShadowSocksSettings,
|
PageProtocolShadowSocksSettings,
|
||||||
PageProtocolCloakSettings,
|
PageProtocolCloakSettings,
|
||||||
PageProtocolWireGuardSettings,
|
PageProtocolWireGuardSettings,
|
||||||
|
PageProtocolAwgSettings,
|
||||||
PageProtocolIKev2Settings,
|
PageProtocolIKev2Settings,
|
||||||
PageProtocolRaw
|
PageProtocolRaw
|
||||||
};
|
};
|
||||||
@@ -84,10 +85,11 @@ public slots:
|
|||||||
void drawerOpen();
|
void drawerOpen();
|
||||||
void drawerClose();
|
void drawerClose();
|
||||||
|
|
||||||
|
|
||||||
bool isTriggeredByConnectButton();
|
bool isTriggeredByConnectButton();
|
||||||
void setTriggeredBtConnectButton(bool trigger);
|
void setTriggeredBtConnectButton(bool trigger);
|
||||||
|
|
||||||
|
void closeApplication();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void goToPage(PageLoader::PageEnum page, bool slide = true);
|
void goToPage(PageLoader::PageEnum page, bool slide = true);
|
||||||
void goToStartPage();
|
void goToStartPage();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
|||||||
m_languageModel(languageModel),
|
m_languageModel(languageModel),
|
||||||
m_settings(settings)
|
m_settings(settings)
|
||||||
{
|
{
|
||||||
m_appVersion = QString("%1: %2 (%3)").arg(tr("Software version"), QString(APP_MAJOR_VERSION), __DATE__);
|
m_appVersion = QString("%1: %2 (%3)").arg(tr("Software version"), QString(APP_VERSION), __DATE__);
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
if (!m_settings->isScreenshotsEnabled()) {
|
if (!m_settings->isScreenshotsEnabled()) {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ void SitesController::addSite(QString hostname)
|
|||||||
QHostInfo::lookupHost(hostname, this, resolveCallback);
|
QHostInfo::lookupHost(hostname, this, resolveCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit finished(tr("New site added: ") + hostname);
|
emit finished(tr("New site added: %1").arg(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SitesController::removeSite(int index)
|
void SitesController::removeSite(int index)
|
||||||
@@ -77,7 +77,7 @@ void SitesController::removeSite(int index)
|
|||||||
Q_ARG(QStringList, QStringList() << hostname));
|
Q_ARG(QStringList, QStringList() << hostname));
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
||||||
|
|
||||||
emit finished(tr("Site removed: ") + hostname);
|
emit finished(tr("Site removed: %1").arg(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||||
@@ -85,19 +85,19 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
|||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
emit errorOccurred(tr("Can't open file: ") + fileName);
|
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = file.readAll();
|
QByteArray jsonData = file.readAll();
|
||||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
||||||
if (jsonDocument.isNull()) {
|
if (jsonDocument.isNull()) {
|
||||||
emit errorOccurred(tr("Failed to parse JSON data from file: ") + fileName);
|
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jsonDocument.isArray()) {
|
if (!jsonDocument.isArray()) {
|
||||||
emit errorOccurred(tr("The JSON data is not an array in file: ") + fileName);
|
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
DockerContainer container = ContainerProps::allContainers().at(index.row());
|
DockerContainer container = ContainerProps::allContainers().at(index.row());
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case NameRole:
|
|
||||||
// return ContainerProps::containerHumanNames().value(container);
|
|
||||||
case DescriptionRole:
|
|
||||||
// return ContainerProps::containerDescriptions().value(container);
|
|
||||||
case ConfigRole: {
|
case ConfigRole: {
|
||||||
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
|
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
|
||||||
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
|
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
|
||||||
@@ -35,19 +31,15 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ServiceTypeRole:
|
case IsDefaultRole: { //todo remove
|
||||||
// return ContainerProps::containerService(container);
|
|
||||||
case DockerContainerRole:
|
|
||||||
// return container;
|
|
||||||
case IsInstalledRole:
|
|
||||||
// return m_settings->containers(m_currentlyProcessedServerIndex).contains(container);
|
|
||||||
case IsDefaultRole: {
|
|
||||||
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
|
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
|
||||||
m_defaultContainerIndex = container;
|
m_defaultContainerIndex = container;
|
||||||
emit defaultContainerChanged();
|
emit defaultContainerChanged();
|
||||||
}
|
}
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit containersModelUpdated();
|
||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -117,6 +109,14 @@ QString ContainersModel::getDefaultContainerName()
|
|||||||
return ContainerProps::containerHumanNames().value(m_defaultContainerIndex);
|
return ContainerProps::containerHumanNames().value(m_defaultContainerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainersModel::setDefaultContainer(int index)
|
||||||
|
{
|
||||||
|
auto container = static_cast<DockerContainer>(index);
|
||||||
|
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
|
||||||
|
m_defaultContainerIndex = container;
|
||||||
|
emit defaultContainerChanged();
|
||||||
|
}
|
||||||
|
|
||||||
int ContainersModel::getCurrentlyProcessedContainerIndex()
|
int ContainersModel::getCurrentlyProcessedContainerIndex()
|
||||||
{
|
{
|
||||||
return m_currentlyProcessedContainerIndex;
|
return m_currentlyProcessedContainerIndex;
|
||||||
@@ -228,6 +228,11 @@ bool ContainersModel::isAnyContainerInstalled()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainersModel::updateContainersConfig()
|
||||||
|
{
|
||||||
|
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> ContainersModel::roleNames() const
|
QHash<int, QByteArray> ContainersModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
DockerContainer getDefaultContainer();
|
DockerContainer getDefaultContainer();
|
||||||
QString getDefaultContainerName();
|
QString getDefaultContainerName();
|
||||||
|
void setDefaultContainer(int index);
|
||||||
|
|
||||||
void setCurrentlyProcessedServerIndex(const int index);
|
void setCurrentlyProcessedServerIndex(const int index);
|
||||||
|
|
||||||
@@ -65,11 +66,14 @@ public slots:
|
|||||||
|
|
||||||
bool isAnyContainerInstalled();
|
bool isAnyContainerInstalled();
|
||||||
|
|
||||||
|
void updateContainersConfig();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void defaultContainerChanged();
|
void defaultContainerChanged();
|
||||||
|
void containersModelUpdated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<DockerContainer, QJsonObject> m_containers;
|
QMap<DockerContainer, QJsonObject> m_containers;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent
|
|||||||
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
|
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
|
||||||
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||||
m_availableLanguages.push_back(
|
m_availableLanguages.push_back(
|
||||||
LanguageModelData { metaEnum.valueToKey(i), static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
LanguageModelData {getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
|
||||||
|
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,11 +37,26 @@ QHash<int, QByteArray> LanguageModel::roleNames() const
|
|||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLanguageEnum language)
|
||||||
|
{
|
||||||
|
QString strLanguage("");
|
||||||
|
switch (language) {
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break;
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; break;
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum language)
|
void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum language)
|
||||||
{
|
{
|
||||||
switch (language) {
|
switch (language) {
|
||||||
case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break;
|
case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break;
|
||||||
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
|
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); break;
|
||||||
default: emit updateTranslations(QLocale::English); break;
|
default: emit updateTranslations(QLocale::English); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +67,7 @@ int LanguageModel::getCurrentLanguageIndex()
|
|||||||
switch (locale.language()) {
|
switch (locale.language()) {
|
||||||
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||||
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
|
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
|
||||||
|
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
|
||||||
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ namespace LanguageSettings
|
|||||||
Q_NAMESPACE
|
Q_NAMESPACE
|
||||||
enum class AvailableLanguageEnum {
|
enum class AvailableLanguageEnum {
|
||||||
English,
|
English,
|
||||||
Russian
|
Russian,
|
||||||
|
China_cn
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(AvailableLanguageEnum)
|
Q_ENUM_NS(AvailableLanguageEnum)
|
||||||
|
|
||||||
@@ -59,6 +60,8 @@ protected:
|
|||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString getLocalLanguageName(const LanguageSettings::AvailableLanguageEnum language);
|
||||||
|
|
||||||
QVector<LanguageModelData> m_availableLanguages;
|
QVector<LanguageModelData> m_availableLanguages;
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#include "awgConfigModel.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
|
AwgConfigModel::AwgConfigModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int AwgConfigModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
|
||||||
|
case Roles::JunkPacketCountRole: m_protocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
|
||||||
|
case Roles::JunkPacketMinSizeRole: m_protocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
|
||||||
|
case Roles::JunkPacketMaxSizeRole: m_protocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
|
||||||
|
case Roles::InitPacketJunkSizeRole:
|
||||||
|
m_protocolConfig.insert(config_key::initPacketJunkSize, value.toString());
|
||||||
|
break;
|
||||||
|
case Roles::ResponsePacketJunkSizeRole:
|
||||||
|
m_protocolConfig.insert(config_key::responsePacketJunkSize, value.toString());
|
||||||
|
break;
|
||||||
|
case Roles::InitPacketMagicHeaderRole:
|
||||||
|
m_protocolConfig.insert(config_key::initPacketMagicHeader, value.toString());
|
||||||
|
break;
|
||||||
|
case Roles::ResponsePacketMagicHeaderRole:
|
||||||
|
m_protocolConfig.insert(config_key::responsePacketMagicHeader, value.toString());
|
||||||
|
break;
|
||||||
|
case Roles::UnderloadPacketMagicHeaderRole:
|
||||||
|
m_protocolConfig.insert(config_key::underloadPacketMagicHeader, value.toString());
|
||||||
|
break;
|
||||||
|
case Roles::TransportPacketMagicHeaderRole:
|
||||||
|
m_protocolConfig.insert(config_key::transportPacketMagicHeader, value.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit dataChanged(index, index, QList { role });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant AwgConfigModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString();
|
||||||
|
case Roles::JunkPacketCountRole: return m_protocolConfig.value(config_key::junkPacketCount);
|
||||||
|
case Roles::JunkPacketMinSizeRole: return m_protocolConfig.value(config_key::junkPacketMinSize);
|
||||||
|
case Roles::JunkPacketMaxSizeRole: return m_protocolConfig.value(config_key::junkPacketMaxSize);
|
||||||
|
case Roles::InitPacketJunkSizeRole: return m_protocolConfig.value(config_key::initPacketJunkSize);
|
||||||
|
case Roles::ResponsePacketJunkSizeRole: return m_protocolConfig.value(config_key::responsePacketJunkSize);
|
||||||
|
case Roles::InitPacketMagicHeaderRole: return m_protocolConfig.value(config_key::initPacketMagicHeader);
|
||||||
|
case Roles::ResponsePacketMagicHeaderRole: return m_protocolConfig.value(config_key::responsePacketMagicHeader);
|
||||||
|
case Roles::UnderloadPacketMagicHeaderRole: return m_protocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||||
|
case Roles::TransportPacketMagicHeaderRole: return m_protocolConfig.value(config_key::transportPacketMagicHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwgConfigModel::updateModel(const QJsonObject &config)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
||||||
|
|
||||||
|
m_fullConfig = config;
|
||||||
|
|
||||||
|
QJsonObject protocolConfig = config.value(config_key::awg).toObject();
|
||||||
|
|
||||||
|
m_protocolConfig[config_key::port] =
|
||||||
|
protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||||
|
m_protocolConfig[config_key::junkPacketCount] =
|
||||||
|
protocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||||
|
m_protocolConfig[config_key::junkPacketMinSize] =
|
||||||
|
protocolConfig.value(config_key::junkPacketMinSize)
|
||||||
|
.toString(protocols::awg::defaultJunkPacketMinSize);
|
||||||
|
m_protocolConfig[config_key::junkPacketMaxSize] =
|
||||||
|
protocolConfig.value(config_key::junkPacketMaxSize)
|
||||||
|
.toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||||
|
m_protocolConfig[config_key::initPacketJunkSize] =
|
||||||
|
protocolConfig.value(config_key::initPacketJunkSize)
|
||||||
|
.toString(protocols::awg::defaultInitPacketJunkSize);
|
||||||
|
m_protocolConfig[config_key::responsePacketJunkSize] =
|
||||||
|
protocolConfig.value(config_key::responsePacketJunkSize)
|
||||||
|
.toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||||
|
m_protocolConfig[config_key::initPacketMagicHeader] =
|
||||||
|
protocolConfig.value(config_key::initPacketMagicHeader)
|
||||||
|
.toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||||
|
m_protocolConfig[config_key::responsePacketMagicHeader] =
|
||||||
|
protocolConfig.value(config_key::responsePacketMagicHeader)
|
||||||
|
.toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||||
|
m_protocolConfig[config_key::underloadPacketMagicHeader] =
|
||||||
|
protocolConfig.value(config_key::underloadPacketMagicHeader)
|
||||||
|
.toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||||
|
m_protocolConfig[config_key::transportPacketMagicHeader] =
|
||||||
|
protocolConfig.value(config_key::transportPacketMagicHeader)
|
||||||
|
.toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject AwgConfigModel::getConfig()
|
||||||
|
{
|
||||||
|
m_fullConfig.insert(config_key::awg, m_protocolConfig);
|
||||||
|
return m_fullConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> AwgConfigModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
|
||||||
|
roles[PortRole] = "port";
|
||||||
|
roles[JunkPacketCountRole] = "junkPacketCount";
|
||||||
|
roles[JunkPacketMinSizeRole] = "junkPacketMinSize";
|
||||||
|
roles[JunkPacketMaxSizeRole] = "junkPacketMaxSize";
|
||||||
|
roles[InitPacketJunkSizeRole] = "initPacketJunkSize";
|
||||||
|
roles[ResponsePacketJunkSizeRole] = "responsePacketJunkSize";
|
||||||
|
roles[InitPacketMagicHeaderRole] = "initPacketMagicHeader";
|
||||||
|
roles[ResponsePacketMagicHeaderRole] = "responsePacketMagicHeader";
|
||||||
|
roles[UnderloadPacketMagicHeaderRole] = "underloadPacketMagicHeader";
|
||||||
|
roles[TransportPacketMagicHeaderRole] = "transportPacketMagicHeader";
|
||||||
|
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef AWGCONFIGMODEL_H
|
||||||
|
#define AWGCONFIGMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "containers/containers_defs.h"
|
||||||
|
|
||||||
|
class AwgConfigModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
PortRole = Qt::UserRole + 1,
|
||||||
|
JunkPacketCountRole,
|
||||||
|
JunkPacketMinSizeRole,
|
||||||
|
JunkPacketMaxSizeRole,
|
||||||
|
InitPacketJunkSizeRole,
|
||||||
|
ResponsePacketJunkSizeRole,
|
||||||
|
InitPacketMagicHeaderRole,
|
||||||
|
ResponsePacketMagicHeaderRole,
|
||||||
|
UnderloadPacketMagicHeaderRole,
|
||||||
|
TransportPacketMagicHeaderRole
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AwgConfigModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateModel(const QJsonObject &config);
|
||||||
|
QJsonObject getConfig();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockerContainer m_container;
|
||||||
|
QJsonObject m_protocolConfig;
|
||||||
|
QJsonObject m_fullConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AWGCONFIGMODEL_H
|
||||||
@@ -78,12 +78,11 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
|
|||||||
case Proto::ShadowSocks: return PageLoader::PageEnum::PageProtocolShadowSocksSettings;
|
case Proto::ShadowSocks: return PageLoader::PageEnum::PageProtocolShadowSocksSettings;
|
||||||
case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardSettings;
|
case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardSettings;
|
||||||
case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings;
|
case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings;
|
||||||
case Proto::L2tp: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
case Proto::L2tp: return PageLoader::PageEnum::PageProtocolIKev2Settings;
|
||||||
// non-vpn
|
// non-vpn
|
||||||
case Proto::TorWebSite: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
|
||||||
case Proto::Dns: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
|
||||||
case Proto::FileShare: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
case Proto::Sftp: return PageLoader::PageEnum::PageServiceSftpSettings;
|
||||||
case Proto::Sftp: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
|
||||||
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ void ServersModel::setDefaultServerIndex(const int index)
|
|||||||
{
|
{
|
||||||
m_settings->setDefaultServer(index);
|
m_settings->setDefaultServer(index);
|
||||||
m_defaultServerIndex = m_settings->defaultServerIndex();
|
m_defaultServerIndex = m_settings->defaultServerIndex();
|
||||||
emit defaultServerIndexChanged();
|
emit defaultServerIndexChanged(m_defaultServerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int ServersModel::getDefaultServerIndex()
|
const int ServersModel::getDefaultServerIndex()
|
||||||
@@ -193,6 +193,12 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
|
|||||||
return primaryDns == protocols::dns::amneziaDnsIp;
|
return primaryDns == protocols::dns::amneziaDnsIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServersModel::updateContainersConfig()
|
||||||
|
{
|
||||||
|
auto server = m_settings->server(m_currentlyProcessedServerIndex);
|
||||||
|
m_servers.replace(m_currentlyProcessedServerIndex, server);
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> ServersModel::roleNames() const
|
QHash<int, QByteArray> ServersModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|||||||
@@ -59,12 +59,14 @@ public slots:
|
|||||||
|
|
||||||
bool isDefaultServerConfigContainsAmneziaDns();
|
bool isDefaultServerConfigContainsAmneziaDns();
|
||||||
|
|
||||||
|
void updateContainersConfig();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void currentlyProcessedServerIndexChanged(const int index);
|
void currentlyProcessedServerIndexChanged(const int index);
|
||||||
void defaultServerIndexChanged();
|
void defaultServerIndexChanged(const int index);
|
||||||
void defaultServerNameChanged();
|
void defaultServerNameChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,7 +3,14 @@
|
|||||||
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
|
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
: QAbstractListModel(parent), m_settings(settings)
|
: QAbstractListModel(parent), m_settings(settings)
|
||||||
{
|
{
|
||||||
m_currentRouteMode = m_settings->routeMode();
|
auto routeMode = m_settings->routeMode();
|
||||||
|
if (routeMode == Settings::RouteMode::VpnAllSites) {
|
||||||
|
m_isSplitTunnelingEnabled = false;
|
||||||
|
m_currentRouteMode = Settings::RouteMode::VpnOnlyForwardSites;
|
||||||
|
} else {
|
||||||
|
m_isSplitTunnelingEnabled = true;
|
||||||
|
m_currentRouteMode = routeMode;
|
||||||
|
}
|
||||||
fillSites();
|
fillSites();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +100,21 @@ void SitesModel::setRouteMode(int routeMode)
|
|||||||
emit routeModeChanged();
|
emit routeModeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SitesModel::isSplitTunnelingEnabled()
|
||||||
|
{
|
||||||
|
return m_isSplitTunnelingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SitesModel::toggleSplitTunneling(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled) {
|
||||||
|
setRouteMode(m_currentRouteMode);
|
||||||
|
} else {
|
||||||
|
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
|
||||||
|
}
|
||||||
|
m_isSplitTunnelingEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
|
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
|
||||||
{
|
{
|
||||||
return m_sites;
|
return m_sites;
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public slots:
|
|||||||
int getRouteMode();
|
int getRouteMode();
|
||||||
void setRouteMode(int routeMode);
|
void setRouteMode(int routeMode);
|
||||||
|
|
||||||
|
bool isSplitTunnelingEnabled();
|
||||||
|
void toggleSplitTunneling(bool enabled);
|
||||||
|
|
||||||
QVector<QPair<QString, QString>> getCurrentSites();
|
QVector<QPair<QString, QString>> getCurrentSites();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -44,6 +47,7 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
|
||||||
|
bool m_isSplitTunnelingEnabled;
|
||||||
Settings::RouteMode m_currentRouteMode;
|
Settings::RouteMode m_currentRouteMode;
|
||||||
|
|
||||||
QVector<QPair<QString, QString>> m_sites;
|
QVector<QPair<QString, QString>> m_sites;
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ void NotificationHandler::setConnectionState(Vpn::ConnectionState state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotificationHandler::onTranslationsUpdated()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void NotificationHandler::unsecuredNetworkNotification(const QString& networkName) {
|
void NotificationHandler::unsecuredNetworkNotification(const QString& networkName) {
|
||||||
qDebug() << "Unsecured network notification shown";
|
qDebug() << "Unsecured network notification shown";
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void setConnectionState(Vpn::ConnectionState state);
|
virtual void setConnectionState(Vpn::ConnectionState state);
|
||||||
|
virtual void onTranslationsUpdated();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void notificationShown(const QString& title, const QString& message);
|
void notificationShown(const QString& title, const QString& message);
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ Button {
|
|||||||
PageController.setTriggeredBtConnectButton(true)
|
PageController.setTriggeredBtConnectButton(true)
|
||||||
|
|
||||||
ServersModel.currentlyProcessedIndex = ServersModel.getDefaultServerIndex()
|
ServersModel.currentlyProcessedIndex = ServersModel.getDefaultServerIndex()
|
||||||
|
InstallController.setShouldCreateServer(false)
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardEasy)
|
PageController.goToPage(PageEnum.PageSetupWizardEasy)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,16 +20,14 @@ DrawerType {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Header2TextType {
|
Header2Type {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 32
|
Layout.bottomMargin: 16
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
text: qsTr("Connection data")
|
headerText: qsTr("Add new connection")
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
@@ -37,7 +35,7 @@ DrawerType {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
text: qsTr("Server IP, login and password")
|
text: qsTr("Configure your server")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
@@ -51,7 +49,7 @@ DrawerType {
|
|||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("QR code, key or configuration file")
|
text: qsTr("Open config file, key or QR code")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|||||||
@@ -50,41 +50,30 @@ ListView {
|
|||||||
imageSource: "qrc:/images/controls/download.svg"
|
imageSource: "qrc:/images/controls/download.svg"
|
||||||
showImage: !isInstalled
|
showImage: !isInstalled
|
||||||
|
|
||||||
checkable: isInstalled
|
checkable: isInstalled && !ConnectionController.isConnected && isSupported
|
||||||
checked: isDefault
|
checked: isDefault
|
||||||
|
|
||||||
onPressed: function(mouse) {
|
|
||||||
if (!isSupported) {
|
|
||||||
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (checked) {
|
if (ConnectionController.isConnected && isInstalled) {
|
||||||
var needReconnected = false
|
PageController.showNotificationMessage(qsTr("Unable change protocol while there is an active connection"))
|
||||||
if (!isDefault) {
|
return
|
||||||
needReconnected = true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
isDefault = true
|
isDefault = true
|
||||||
|
|
||||||
menuContent.currentIndex = index
|
menuContent.currentIndex = index
|
||||||
containersDropDown.menuVisible = false
|
containersDropDown.menuVisible = false
|
||||||
|
|
||||||
|
|
||||||
if (needReconnected &&
|
|
||||||
(ConnectionController.isConnected || ConnectionController.isConnectionInProgress)) {
|
|
||||||
PageController.showNotificationMessage(qsTr("Reconnect via VPN Procotol: ") + name)
|
|
||||||
PageController.goToPageHome()
|
|
||||||
menu.visible = false
|
|
||||||
ConnectionController.openConnection()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
if (!isSupported && isInstalled) {
|
||||||
|
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(index))
|
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(index))
|
||||||
InstallController.setShouldCreateServer(false)
|
InstallController.setShouldCreateServer(false)
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||||
containersDropDown.menuVisible = false
|
containersDropDown.menuVisible = false
|
||||||
menu.visible = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user