Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0275ed6170 | |||
| 7035abd1fe | |||
| c88150e059 | |||
| 5b663f6397 | |||
| 1b2abe1b14 | |||
| 62d7df1cdb | |||
| 831c9a09fb | |||
| 8a713b74f2 | |||
| 09ac1fa679 | |||
| fe209de9bd | |||
| 9b7f4c509f | |||
| e7c3911091 | |||
| 1858d6f22e | |||
| 568628df78 | |||
| 5aa5699620 | |||
| 84d1e28de3 | |||
| b0849f4767 | |||
| 6e8266a6ae | |||
| d8b21b5298 | |||
| 5e546468e9 | |||
| 86ba47fab9 | |||
| 026175af20 | |||
| 1d7c7cc4fb | |||
| 2c9f7f59cb | |||
| 0e44177d07 | |||
| 5b298883b0 | |||
| 44744fd69a | |||
| 87c9a598fe | |||
| 42540fb22f | |||
| 33840884ed | |||
| 1c5dd849b0 | |||
| 030abcbdb6 | |||
| 980bf368f4 | |||
| afd129a402 | |||
| d2ce6cc2de | |||
| 1afa5e46d5 | |||
| c3e4f315ed | |||
| e0983b74fb | |||
| 31c672dab8 | |||
| f3a058ba55 | |||
| 46b0d24482 | |||
| d4472b62b3 | |||
| 866a1ee84b | |||
| bd88bd7d37 | |||
| 161cba9bbf | |||
| 06bf3e5e90 | |||
| 559f230aca | |||
| 19b1c94100 | |||
| 3ae3110ec0 | |||
| d24dd5236b | |||
| e08bf6de07 | |||
| 5e5fb917fe | |||
| 5ab4318c23 | |||
| 7261029082 | |||
| c55b025eee | |||
| fc6fc26148 | |||
| 48b43ee102 | |||
| e091020692 | |||
| 703b9137e0 | |||
| f163f0fc1d | |||
| 3b49d5ca59 | |||
| 236e5ca2e3 | |||
| 2f6e28b980 | |||
| 46d96a8887 | |||
| 56221881da | |||
| b173dcaa17 | |||
| da5fe1d766 | |||
| a15ea0e8a1 | |||
| fbbba648c4 | |||
| f79bfa9d2e | |||
| 3011a0e306 | |||
| 76640311ab | |||
| e707471b04 | |||
| 6425700d1c | |||
| 36045c6694 | |||
| 52ecd6899b | |||
| 49a6a9ed76 | |||
| 4869429eb6 | |||
| 956dd6e37a | |||
| 665a2911be | |||
| 1cfa4e0630 | |||
| 5bda624576 | |||
| d1f0560595 | |||
| df07fc1b1f | |||
| 8ca31e0c90 | |||
| f1c6067485 | |||
| ca04c63f5e | |||
| 89cdd2bece | |||
| 73d7dfa54f | |||
| 0a5b54a2e4 | |||
| e43aa02a5b | |||
| c3fb62a6ab | |||
| 62f3a339b7 | |||
| 767b14b37a | |||
| e7fa160c9c | |||
| 7350d79c50 | |||
| 86f08554cd | |||
| a741186c21 | |||
| 6acaab0ffa | |||
| 212e9b3a91 | |||
| 2bff37efae | |||
| b88ab8e432 | |||
| 48f6cf904e | |||
| 367789bda2 | |||
| d06924c59d | |||
| 2db99715b1 | |||
| 9688a8e52d | |||
| b2c429f74d | |||
| 6ea6ab1bd9 | |||
| c5aa070bf4 | |||
| d67201ede9 | |||
| 4323fb2063 | |||
| 6d5452b8ee | |||
| 569d63ef0f | |||
| ea910ba300 | |||
| 1c1e74d06f | |||
| 5dc16c06f1 |
@@ -0,0 +1,39 @@
|
|||||||
|
BasedOnStyle: WebKit
|
||||||
|
AccessModifierOffset: '-4'
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: 'true'
|
||||||
|
AlignTrailingComments: 'true'
|
||||||
|
AllowAllArgumentsOnNextLine: 'true'
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||||
|
AllowShortBlocksOnASingleLine: 'false'
|
||||||
|
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||||
|
AllowShortEnumsOnASingleLine: 'false'
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AlwaysBreakTemplateDeclarations: 'No'
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
ColumnLimit: '120'
|
||||||
|
CommentPragmas: '"^!|^:"'
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||||
|
ConstructorInitializerIndentWidth: '4'
|
||||||
|
ContinuationIndentWidth: '8'
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
NamespaceIndentation: All
|
||||||
|
PenaltyExcessCharacter: '10'
|
||||||
|
PointerAlignment: Right
|
||||||
|
SortIncludes: 'true'
|
||||||
|
SpaceAfterTemplateKeyword: 'false'
|
||||||
|
Standard: Auto
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/client/3rd
|
||||||
|
/client/3rd-prebuild
|
||||||
|
/client/android
|
||||||
|
/client/cmake
|
||||||
|
/client/core/serialization
|
||||||
|
/client/daemon
|
||||||
|
/client/fonts
|
||||||
|
/client/images
|
||||||
|
/client/ios
|
||||||
|
/client/mozilla
|
||||||
|
/client/platforms/dummy
|
||||||
|
/client/platforms/linux
|
||||||
|
/client/platforms/macos
|
||||||
|
/client/platforms/windows
|
||||||
|
/client/server_scripts
|
||||||
|
/client/translations
|
||||||
|
/deploy
|
||||||
|
/docs
|
||||||
|
/metadata
|
||||||
|
/service/src
|
||||||
@@ -16,10 +16,7 @@ jobs:
|
|||||||
QT_VERSION: 6.6.2
|
QT_VERSION: 6.6.2
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
@@ -86,10 +83,7 @@ jobs:
|
|||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
BUILD_ARCH: 64
|
BUILD_ARCH: 64
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
@@ -145,23 +139,20 @@ jobs:
|
|||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-iOS:
|
Build-iOS:
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.6.2
|
QT_VERSION: 6.8.0
|
||||||
CC: cc
|
CC: cc
|
||||||
CXX: c++
|
CXX: c++
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: '15.2'
|
xcode-version: '15.4.0'
|
||||||
|
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -217,11 +208,7 @@ jobs:
|
|||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
|
||||||
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
||||||
export PATH=$PATH:~/go/bin
|
export PATH=$PATH:~/go/bin
|
||||||
sh deploy/build_ios.sh | \
|
sh deploy/build_ios.sh
|
||||||
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
|
|
||||||
env:
|
env:
|
||||||
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
||||||
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
||||||
@@ -248,19 +235,16 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||||
QT_VERSION: 6.4.3
|
QT_VERSION: 6.8.0
|
||||||
QIF_VERSION: 4.6
|
QIF_VERSION: 4.6
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: '15.4.0'
|
xcode-version: '16.1.0'
|
||||||
|
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -310,6 +294,78 @@ jobs:
|
|||||||
path: deploy/build/client/AmneziaVPN.app
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
Build-MacOS-NE:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.8.0
|
||||||
|
QIF_VERSION: 4.6
|
||||||
|
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup Xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '16.1.0'
|
||||||
|
|
||||||
|
- name: 'Install desktop Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia qtimageformats'
|
||||||
|
arch: 'clang_64'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||||
|
run: |
|
||||||
|
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||||
|
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||||
|
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||||
|
- name: 'Install Go'
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22.1'
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Install dependencies'
|
||||||
|
run: pip install jsonschema jinja2
|
||||||
|
|
||||||
|
- name: 'Set execute permissions for deploy script'
|
||||||
|
run: chmod +x deploy/build_macos_ne.sh
|
||||||
|
|
||||||
|
- name: 'Build and deploy macOS NE'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
||||||
|
bash deploy/build_macos_ne.sh
|
||||||
|
env:
|
||||||
|
MAC_TRUST_CERT_BASE64: ${{ secrets.MAC_TRUST_CERT_BASE64 }}
|
||||||
|
MAC_SIGNING_CERT_BASE64: ${{ secrets.MAC_SIGNING_CERT_BASE64 }}
|
||||||
|
MAC_SIGNING_CERT_PASSWORD: ${{ secrets.MAC_SIGNING_CERT_PASSWORD }}
|
||||||
|
APPSTORE_CONNECT_MAC_PROVISIONING_BASE64: ${{ secrets.APPSTORE_CONNECT_MAC_PROVISIONING }}
|
||||||
|
APPSTORE_CONNECT_MAC_NE_PROVISIONING_BASE64: ${{ secrets.APPSTORE_CONNECT_MAC_NE_PROVISIONING }}
|
||||||
|
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
||||||
|
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
||||||
|
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
||||||
|
- name: 'Upload macOS .dmg and dSYMs to artifacts'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos dmg & dsyms
|
||||||
|
path: |
|
||||||
|
${{ github.workspace }}/AmneziaVPN.dmg
|
||||||
|
retention-days: 7
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
@@ -317,13 +373,10 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-34
|
ANDROID_BUILD_PLATFORM: android-34
|
||||||
QT_VERSION: 6.7.3
|
QT_VERSION: 6.7.2
|
||||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
@@ -480,4 +533,4 @@ jobs:
|
|||||||
if: ${{ fromJSON(steps.pull_request.outputs.data)[0].number != '' }}
|
if: ${{ fromJSON(steps.pull_request.outputs.data)[0].number != '' }}
|
||||||
run: |
|
run: |
|
||||||
echo "Pull request:" >> $GITHUB_STEP_SUMMARY
|
echo "Pull request:" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "[[#${{ fromJSON(steps.pull_request.outputs.data)[0].number }}] ${{ fromJSON(steps.pull_request.outputs.data)[0].title }}](${{ fromJSON(steps.pull_request.outputs.data)[0].html_url }})" >> $GITHUB_STEP_SUMMARY
|
echo "[[#${{ fromJSON(steps.pull_request.outputs.data)[0].number }}] ${{ fromJSON(steps.pull_request.outputs.data)[0].title }}](${{ fromJSON(steps.pull_request.outputs.data)[0].html_url }})" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
[submodule "client/3rd/OpenVPNAdapter"]
|
|
||||||
path = client/3rd/OpenVPNAdapter
|
|
||||||
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
|
||||||
[submodule "client/3rd/qtkeychain"]
|
[submodule "client/3rd/qtkeychain"]
|
||||||
path = client/3rd/qtkeychain
|
path = client/3rd/qtkeychain
|
||||||
url = https://github.com/frankosterfeld/qtkeychain.git
|
url = https://github.com/frankosterfeld/qtkeychain.git
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.2.4
|
project(${PROJECT} VERSION 4.8.3.2
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2071)
|
set(APP_ANDROID_VERSION_CODE 2075)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -31,13 +31,13 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(APPLE AND NOT IOS)
|
if((APPLE AND NOT IOS) OR (DEFINED MACOS_NE AND MACOS_NE AND NOT IOS))
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
# Amnezia VPN
|
# Amnezia VPN
|
||||||
## _The best client for self-hosted VPN_
|
|
||||||
|
### _The best client for self-hosted VPN_
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
||||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||||
|
|
||||||
|
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
|
||||||
|
|
||||||
|
|
||||||
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
||||||
|
|
||||||
[](https://amnezia.org)
|
[](https://amnezia.org)
|
||||||
@@ -180,7 +185,7 @@ GPL v3.0
|
|||||||
|
|
||||||
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
||||||
|
|
||||||
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
|
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
||||||
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
||||||
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
||||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# Amnezia VPN
|
||||||
|
|
||||||
|
### _Лучший клиент для создания VPN на собственном сервере_
|
||||||
|
|
||||||
|
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
||||||
|
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||||
|
|
||||||
|
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||||
|
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||||
|
|
||||||
|
[](https://amnezia.org)
|
||||||
|
|
||||||
|
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/kldscp/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/kldscp/amnezia.org).
|
||||||
|
|
||||||
|
<a href="https://storage.googleapis.com/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
||||||
|
|
||||||
|
|
||||||
|
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
|
||||||
|
|
||||||
|
## Особенности
|
||||||
|
|
||||||
|
- Простой в использовании — введите IP-адрес, SSH-логин и пароль, и Amnezia автоматически установит VPN-контейнеры Docker на ваш сервер и подключится к VPN.
|
||||||
|
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
|
||||||
|
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
|
||||||
|
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
||||||
|
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
|
||||||
|
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
||||||
|
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
||||||
|
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||||
|
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский)
|
||||||
|
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси)
|
||||||
|
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
|
||||||
|
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский)
|
||||||
|
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
|
||||||
|
|
||||||
|
## Технологии
|
||||||
|
|
||||||
|
AmneziaVPN использует несколько проектов с открытым исходным кодом:
|
||||||
|
|
||||||
|
- [OpenSSL](https://www.openssl.org/)
|
||||||
|
- [OpenVPN](https://openvpn.net/)
|
||||||
|
- [Shadowsocks](https://shadowsocks.org/)
|
||||||
|
- [Qt](https://www.qt.io/)
|
||||||
|
- [LibSsh](https://libssh.org)
|
||||||
|
- и другие...
|
||||||
|
|
||||||
|
## Проверка исходного кода
|
||||||
|
После клонирования репозитория обязательно загрузите все подмодули.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
Хотите внести свой вклад? Добро пожаловать!
|
||||||
|
|
||||||
|
### Помощь с переводами
|
||||||
|
|
||||||
|
Загрузите самые актуальные файлы перевода.
|
||||||
|
|
||||||
|
Перейдите на [вкладку "Actions"](https://github.com/amnezia-vpn/amnezia-client/actions?query=is%3Asuccess+branch%3Adev), нажмите на первую строку. Затем прокрутите вниз до раздела "Artifacts" и скачайте "AmneziaVPN_translations".
|
||||||
|
|
||||||
|
Распакуйте этот файл. Каждый файл с расширением *.ts содержит строки для соответствующего языка.
|
||||||
|
|
||||||
|
Переведите или исправьте строки в одном или нескольких файлах *.ts и загрузите их обратно в этот репозиторий в папку ``client/translations``. Это можно сделать через веб-интерфейс или любым другим знакомым вам способом.
|
||||||
|
|
||||||
|
### Сборка исходного кода и деплой
|
||||||
|
Проверьте папку deploy для скриптов сборки.
|
||||||
|
|
||||||
|
### Как собрать iOS-приложение из исходного кода на MacOS
|
||||||
|
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
|
||||||
|
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||||
|
- MacOS
|
||||||
|
- iOS
|
||||||
|
- Модуль совместимости с Qt 5
|
||||||
|
- Qt Shader Tools
|
||||||
|
- Дополнительные библиотеки:
|
||||||
|
- Qt Image Formats
|
||||||
|
- Qt Multimedia
|
||||||
|
- Qt Remote Objects
|
||||||
|
|
||||||
|
|
||||||
|
3. Установите CMake, если это необходимо. Рекомендуемая версия — 3.25. Скачать CMake можно здесь.
|
||||||
|
4. Установите Go версии >= v1.16. Если Go ещё не установлен, скачайте его с [официального сайта](https://golang.org/dl/) или используйте Homebrew. Установите gomobile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH=$PATH:~/go/bin
|
||||||
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||||
|
gomobile init
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Соберите проект:
|
||||||
|
```bash
|
||||||
|
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
|
||||||
|
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
|
||||||
|
export QT_IOS_BIN=$QT_BIN_DIR
|
||||||
|
export PATH=$PATH:~/go/bin
|
||||||
|
mkdir build-ios
|
||||||
|
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||||
|
```
|
||||||
|
Замените <PATH-TO-QT-FOLDER> и <QT-VERSION> на ваши значения.
|
||||||
|
|
||||||
|
Если появляется ошибка gomobile: command not found, убедитесь, что PATH настроен на папку bin, где установлен gomobile:
|
||||||
|
```bash
|
||||||
|
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||||
|
|
||||||
|
Если сборка завершится с ошибкой:
|
||||||
|
```
|
||||||
|
make: ***
|
||||||
|
[$(PROJECTDIR)/client/build/AmneziaVPN.build/Debug-iphoneos/wireguard-go-bridge/goroot/.prepared]
|
||||||
|
Error 1
|
||||||
|
```
|
||||||
|
Добавьте пользовательскую переменную PATH в настройки сборки для целей AmneziaVPN и WireGuardNetworkExtension с ключом `PATH` и значением `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`.
|
||||||
|
|
||||||
|
Если ошибка повторяется на Mac с M1, установите версию CMake для архитектуры ARM:
|
||||||
|
```
|
||||||
|
arch -arm64 brew install cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
При первой попытке сборка может завершиться с ошибкой source files not found. Это происходит из-за параллельной компиляции зависимостей в XCode. Просто перезапустите сборку.
|
||||||
|
|
||||||
|
|
||||||
|
## Как собрать Android-приложение
|
||||||
|
Сборка тестировалась на MacOS. Требования:
|
||||||
|
- JDK 11
|
||||||
|
- Android SDK 33
|
||||||
|
- CMake 3.25.0
|
||||||
|
|
||||||
|
Установите QT, QT Creator и Android Studio.
|
||||||
|
Настройте QT Creator:
|
||||||
|
|
||||||
|
- В меню QT Creator перейдите в `QT Creator` -> `Preferences` -> `Devices` ->`Android`.
|
||||||
|
- Укажите путь к JDK 11.
|
||||||
|
- Укажите путь к Android SDK (`$ANDROID_HOME`)
|
||||||
|
|
||||||
|
Если вы сталкиваетесь с ошибками, связанными с отсутствием SDK или сообщением «SDK manager not running», их нельзя исправить просто корректировкой путей. Если у вас есть несколько свободных гигабайт на диске, вы можете позволить Qt Creator установить все необходимые компоненты, выбрав пустую папку для расположения Android SDK и нажав кнопку **Set Up SDK**. Учтите: это установит второй Android SDK и NDK на вашем компьютере!
|
||||||
|
|
||||||
|
Убедитесь, что настроена правильная версия CMake: перейдите в **Qt Creator -> Preferences** и в боковом меню выберите пункт **Kits**. В центральной части окна, на вкладке **Kits**, найдите запись для инструмента **CMake Tool**. Если выбранная по умолчанию версия CMake ниже 3.25.0, установите на свою систему CMake версии 3.25.0 или выше, а затем выберите опцию **System CMake at <путь>** из выпадающего списка. Если этот пункт отсутствует, это может означать, что вы еще не установили CMake, или Qt Creator не смог найти путь к нему. В таком случае в окне **Preferences** перейдите в боковое меню **CMake**, затем во вкладку **Tools** в центральной части окна и нажмите кнопку **Add**, чтобы указать путь к установленному CMake.
|
||||||
|
|
||||||
|
Убедитесь, что для вашего проекта выбрана Android Platform SDK 33: в главном окне на боковой панели выберите пункт **Projects**, и слева вы увидите раздел **Build & Run**, показывающий различные целевые Android-платформы. Вы можете выбрать любую из них, так как настройка проекта Amnezia VPN разработана таким образом, чтобы все Android-цели могли быть собраны. Перейдите в подраздел **Build** и прокрутите центральную часть окна до раздела **Build Steps**. Нажмите **Details** в заголовке **Build Android APK** (кнопка **Details** может быть скрыта, если окно Qt Creator не запущено в полноэкранном режиме!). Вот здесь выберите **android-33** в качестве Android Build Platform SDK.
|
||||||
|
|
||||||
|
### Разработка Android-компонентов
|
||||||
|
|
||||||
|
После сборки QT Creator копирует проект в отдельную папку, например, `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>`. Для разработки Android-компонентов откройте сгенерированный проект в Android Studio, указав папку `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>/client/android-build` в качестве корневой.
|
||||||
|
Изменения в сгенерированном проекте нужно вручную перенести в репозиторий. После этого можно коммитить изменения.
|
||||||
|
Если возникают проблемы со сборкой в QT Creator после работы в Android Studio, выполните команду `./gradlew clean` в корневой папке сгенерированного проекта (`<path>/client/android-build/.`).
|
||||||
|
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
GPL v3.0
|
||||||
|
|
||||||
|
## Донаты
|
||||||
|
|
||||||
|
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
||||||
|
|
||||||
|
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
||||||
|
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
||||||
|
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
||||||
|
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
||||||
|
TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
|
||||||
|
|
||||||
|
## Благодарности
|
||||||
|
|
||||||
|
Этот проект тестируется с помощью BrowserStack.
|
||||||
|
Мы выражаем благодарность [BrowserStack](https://www.browserstack.com) за поддержку нашего проекта.
|
||||||
@@ -25,17 +25,13 @@ execute_process(
|
|||||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||||
|
|
||||||
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
|
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
|
||||||
|
|
||||||
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||||
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
|
||||||
|
|
||||||
if(IOS)
|
#Macos Network Extension doesn't need Widgets
|
||||||
set(PACKAGES ${PACKAGES} Multimedia)
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID) OR (NOT MACOS_NE))
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
|
||||||
set(PACKAGES ${PACKAGES} Widgets)
|
set(PACKAGES ${PACKAGES} Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -48,18 +44,16 @@ set(LIBS ${LIBS}
|
|||||||
Qt6::Core5Compat Qt6::Concurrent
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
if(IOS)
|
#Macos Network Extension doesn't need Widgets
|
||||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID) OR (APPLE AND NOT MACOS_NE))
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
|
||||||
set(LIBS ${LIBS} Qt6::Widgets)
|
set(LIBS ${LIBS} Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
|
message("Run this block when MACOS_NE is not defined or set to FALSE")
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
||||||
@@ -97,10 +91,18 @@ qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
|||||||
# -- i18n end
|
# -- i18n end
|
||||||
|
|
||||||
if(IOS)
|
if(IOS)
|
||||||
|
message("Building for iOS")
|
||||||
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()
|
||||||
|
|
||||||
|
# Build openvpn adapter for MacOS Network Extension
|
||||||
|
if(MACOS_NE)
|
||||||
|
message("Building for MacOS Network Extension")
|
||||||
|
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/macos/scripts/openvpn.sh args
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
set(IS_CI ${CI})
|
set(IS_CI ${CI})
|
||||||
if(IS_CI)
|
if(IS_CI)
|
||||||
message("Detected CI env")
|
message("Detected CI env")
|
||||||
@@ -161,12 +163,24 @@ include_directories(mozilla)
|
|||||||
include_directories(mozilla/shared)
|
include_directories(mozilla/shared)
|
||||||
include_directories(mozilla/models)
|
include_directories(mozilla/models)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(MACOS_NE)
|
||||||
|
message("MACOS_NE is ON")
|
||||||
|
add_definitions(-DQ_OS_MAC)
|
||||||
|
add_definitions(-DMACOS_NE)
|
||||||
|
message("Add macros for MacOS Network Extension")
|
||||||
|
else()
|
||||||
|
message("MACOS_NE is OFF")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
|
message(" Add header for non-IOS and non-MACOS_NE")
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||||
@@ -211,7 +225,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS)
|
if((NOT IOS) OR (NOT MACOS_NE))
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
)
|
)
|
||||||
@@ -312,6 +326,7 @@ if(APPLE)
|
|||||||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||||
|
|
||||||
|
set(MACOSX_DEPLOYMENT_TARGET "12.0")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX AND NOT ANDROID)
|
if(LINUX AND NOT ANDROID)
|
||||||
@@ -319,10 +334,9 @@ if(LINUX AND NOT ANDROID)
|
|||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
# Macos Network Extension doesn't need
|
||||||
message("Client desktop build")
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
|
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
|
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
|
||||||
@@ -355,9 +369,11 @@ endif()
|
|||||||
if(IOS)
|
if(IOS)
|
||||||
include(cmake/ios.cmake)
|
include(cmake/ios.cmake)
|
||||||
include(cmake/ios-arch-fixup.cmake)
|
include(cmake/ios-arch-fixup.cmake)
|
||||||
elseif(APPLE AND NOT IOS)
|
elseif(APPLE AND NOT IOS AND NOT DEFINED MACOS_NE)
|
||||||
include(cmake/osxtools.cmake)
|
# include(cmake/osxtools.cmake)
|
||||||
include(cmake/macos.cmake)
|
include(cmake/macos.cmake)
|
||||||
|
elseif(APPLE AND NOT IOS AND MACOS_NE)
|
||||||
|
include(cmake/macos_ne.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
@@ -376,7 +392,7 @@ elseif(APPLE AND NOT IOS)
|
|||||||
set(DEPLOY_PLATFORM_PATH "macos")
|
set(DEPLOY_PLATFORM_PATH "macos")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT} POST_BUILD
|
TARGET ${PROJECT} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
@@ -395,4 +411,35 @@ if(NOT IOS AND NOT ANDROID)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
if(MACOS_NE)
|
||||||
|
message("Copy MacOS Network Extension files")
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||||
|
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "Copying OpenVPNAdapter.framework..."
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_SOURCE_DIR}/client/3rd/OpenVPNAdapter/build/Release-macos/OpenVPNAdapter.framework/Versions/A
|
||||||
|
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/A
|
||||||
|
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "OpenVPNAdapter.framework copied successfully."
|
||||||
|
)
|
||||||
|
|
||||||
|
# MacOS specific application deployment
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# MacOS specific code signing for Release
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
SET(SIGN_CMD codesign --deep --force --sign 'Apple Distribution: Privacy Technologies OU \(X7UJ388FXK\)' --timestamp --options runtime $<TARGET_BUNDLE_DIR:AmneziaVPN>)
|
||||||
|
message("Manual signing bundle...")
|
||||||
|
message(${SIGN_CMD})
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${SIGN_CMD}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_finalize_target(${PROJECT})
|
qt_finalize_target(${PROJECT})
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#include "protocols/qml_register_protocols.h"
|
#include "protocols/qml_register_protocols.h"
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -121,7 +121,8 @@ void AmneziaApplication::init()
|
|||||||
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS)
|
||||||
|
#if defined(MACOS_NE)
|
||||||
IosController::Instance()->initialize();
|
IosController::Instance()->initialize();
|
||||||
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
||||||
emit m_pageController->goToPageHome();
|
emit m_pageController->goToPageHome();
|
||||||
@@ -134,6 +135,7 @@ void AmneziaApplication::init()
|
|||||||
m_pageController->goToPageSettingsBackup();
|
m_pageController->goToPageSettingsBackup();
|
||||||
emit m_settingsController->importBackupFromOutside(filePath);
|
emit m_settingsController->importBackupFromOutside(filePath);
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ bool AmneziaApplication::parseCommands()
|
|||||||
|
|
||||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||||
m_parser.addOption(c_cleanup);
|
m_parser.addOption(c_cleanup);
|
||||||
|
|
||||||
m_parser.process(*this);
|
m_parser.process(*this);
|
||||||
|
|
||||||
if (m_parser.isSet(c_cleanup)) {
|
if (m_parser.isSet(c_cleanup)) {
|
||||||
@@ -281,7 +283,7 @@ bool AmneziaApplication::parseCommands()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void AmneziaApplication::startLocalServer() {
|
void AmneziaApplication::startLocalServer() {
|
||||||
const QString serverName("AmneziaVPNInstance");
|
const QString serverName("AmneziaVPNInstance");
|
||||||
QLocalServer::removeServer(serverName);
|
QLocalServer::removeServer(serverName);
|
||||||
@@ -404,6 +406,9 @@ void AmneziaApplication::initControllers()
|
|||||||
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());
|
||||||
|
|
||||||
|
m_focusController.reset(new FocusController(m_engine, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
|
||||||
|
|
||||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
|
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
|
||||||
m_apiServicesModel, m_settings));
|
m_apiServicesModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "ui/controllers/exportController.h"
|
#include "ui/controllers/exportController.h"
|
||||||
#include "ui/controllers/importController.h"
|
#include "ui/controllers/importController.h"
|
||||||
#include "ui/controllers/installController.h"
|
#include "ui/controllers/installController.h"
|
||||||
|
#include "ui/controllers/focusController.h"
|
||||||
#include "ui/controllers/pageController.h"
|
#include "ui/controllers/pageController.h"
|
||||||
#include "ui/controllers/settingsController.h"
|
#include "ui/controllers/settingsController.h"
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||||
#else
|
#else
|
||||||
#define AMNEZIA_BASE_CLASS QApplication
|
#define AMNEZIA_BASE_CLASS QApplication
|
||||||
@@ -70,7 +71,7 @@ public:
|
|||||||
void updateTranslator(const QLocale &locale);
|
void updateTranslator(const QLocale &locale);
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void startLocalServer();
|
void startLocalServer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -124,6 +125,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
QScopedPointer<ConnectionController> m_connectionController;
|
QScopedPointer<ConnectionController> m_connectionController;
|
||||||
|
QScopedPointer<FocusController> m_focusController;
|
||||||
QScopedPointer<PageController> m_pageController;
|
QScopedPointer<PageController> m_pageController;
|
||||||
QScopedPointer<InstallController> m_installController;
|
QScopedPointer<InstallController> m_installController;
|
||||||
QScopedPointer<ImportController> m_importController;
|
QScopedPointer<ImportController> m_importController;
|
||||||
|
|||||||
@@ -91,6 +91,13 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Translucent" />
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
|
<activity android:name=".TvFilePicker"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImportConfigActivity"
|
android:name=".ImportConfigActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_banner_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -23,4 +23,6 @@
|
|||||||
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
||||||
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
||||||
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
||||||
|
|
||||||
|
<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_banner_background">#1E1E1F</color>
|
|
||||||
</resources>
|
|
||||||
@@ -23,4 +23,6 @@
|
|||||||
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
||||||
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
||||||
<string name="openNotificationSettings">Open notification settings</string>
|
<string name="openNotificationSettings">Open notification settings</string>
|
||||||
|
|
||||||
|
<string name="tvNoFileBrowser">Please install a file management utility to browse files</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -4,6 +4,7 @@ import android.Manifest
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -12,6 +13,7 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
|||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -20,8 +22,13 @@ import android.os.IBinder
|
|||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager.LayoutParams
|
import android.view.WindowManager.LayoutParams
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -30,6 +37,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.text.RegexOption.IGNORE_CASE
|
import kotlin.text.RegexOption.IGNORE_CASE
|
||||||
import AppListProvider
|
import AppListProvider
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
@@ -71,6 +79,7 @@ class AmneziaActivity : QtActivity() {
|
|||||||
private var isInBoundState = false
|
private var isInBoundState = false
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
private var notificationStateReceiver: BroadcastReceiver? = null
|
||||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||||
|
private var pfd: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||||
@@ -514,21 +523,25 @@ class AmneziaActivity : QtActivity() {
|
|||||||
type = "text/*"
|
type = "text/*"
|
||||||
putExtra(Intent.EXTRA_TITLE, fileName)
|
putExtra(Intent.EXTRA_TITLE, fileName)
|
||||||
}.also {
|
}.also {
|
||||||
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
try {
|
||||||
onSuccess = {
|
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
it?.data?.let { uri ->
|
onSuccess = {
|
||||||
Log.v(TAG, "Save file to $uri")
|
it?.data?.let { uri ->
|
||||||
try {
|
Log.v(TAG, "Save file to $uri")
|
||||||
contentResolver.openOutputStream(uri)?.use { os ->
|
try {
|
||||||
os.bufferedWriter().use { it.write(data) }
|
contentResolver.openOutputStream(uri)?.use { os ->
|
||||||
|
os.bufferedWriter().use { it.write(data) }
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Failed to save file $uri: $e")
|
||||||
|
// todo: send error to Qt
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Failed to save file $uri: $e")
|
|
||||||
// todo: send error to Qt
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
))
|
||||||
))
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,35 +550,46 @@ class AmneziaActivity : QtActivity() {
|
|||||||
fun openFile(filter: String?) {
|
fun openFile(filter: String?) {
|
||||||
Log.v(TAG, "Open file with filter: $filter")
|
Log.v(TAG, "Open file with filter: $filter")
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
val intent = if (!isOnTv()) {
|
||||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||||
val mime = MimeTypeMap.getSingleton()
|
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||||
extensionRegex.findAll(filter).map {
|
val mime = MimeTypeMap.getSingleton()
|
||||||
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
extensionRegex.findAll(filter).map {
|
||||||
}.toSet()
|
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
||||||
} else emptySet()
|
}.toSet()
|
||||||
|
} else emptySet()
|
||||||
|
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||||
if ("*/*" in mimeTypes) {
|
if ("*/*" in mimeTypes) {
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
} else {
|
} else {
|
||||||
when (mimeTypes.size) {
|
when (mimeTypes.size) {
|
||||||
1 -> type = mimeTypes.first()
|
1 -> type = mimeTypes.first()
|
||||||
|
|
||||||
in 2..Int.MAX_VALUE -> {
|
in 2..Int.MAX_VALUE -> {
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> type = "*/*"
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> type = "*/*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also {
|
} else {
|
||||||
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
Intent(this@AmneziaActivity, TvFilePicker::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
onAny = {
|
onAny = {
|
||||||
val uri = it?.data?.toString() ?: ""
|
if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
|
||||||
|
showNoFileBrowserAlertDialog()
|
||||||
|
}
|
||||||
|
val uri = it?.data?.apply {
|
||||||
|
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}?.toString() ?: ""
|
||||||
Log.v(TAG, "Open file: $uri")
|
Log.v(TAG, "Open file: $uri")
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
qtInitialized.await()
|
qtInitialized.await()
|
||||||
@@ -573,10 +597,68 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
showNoFileBrowserAlertDialog()
|
||||||
|
mainScope.launch {
|
||||||
|
qtInitialized.await()
|
||||||
|
QtAndroidController.onFileOpened("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showNoFileBrowserAlertDialog() {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(R.string.tvNoFileBrowser)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
try {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun getFd(fileName: String): Int {
|
||||||
|
Log.v(TAG, "Get fd for $fileName")
|
||||||
|
return blockingCall {
|
||||||
|
try {
|
||||||
|
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
|
||||||
|
pfd?.fd ?: -1
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get fd: $e")
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun closeFd() {
|
||||||
|
Log.v(TAG, "Close fd")
|
||||||
|
mainScope.launch {
|
||||||
|
pfd?.close()
|
||||||
|
pfd = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun getFileName(uri: String): String {
|
||||||
|
Log.v(TAG, "Get file name for uri: $uri")
|
||||||
|
return blockingCall {
|
||||||
|
try {
|
||||||
|
contentResolver.query(Uri.parse(uri), arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
|
||||||
|
if (cursor.moveToFirst() && !cursor.isNull(0)) {
|
||||||
|
return@blockingCall cursor.getString(0) ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get file name: $e")
|
||||||
|
}
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||||
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
||||||
@@ -721,6 +803,50 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// method to workaround Qt's problem with calling the keyboard on TVs
|
||||||
|
@Suppress("unused")
|
||||||
|
fun sendTouch(x: Float, y: Float) {
|
||||||
|
Log.v(TAG, "Send touch: $x, $y")
|
||||||
|
blockingCall {
|
||||||
|
findQtWindow(window.decorView)?.let {
|
||||||
|
Log.v(TAG, "Send touch to $it")
|
||||||
|
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN))
|
||||||
|
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findQtWindow(view: View): View? {
|
||||||
|
Log.v(TAG, "findQtWindow: process $view")
|
||||||
|
if (view::class.simpleName == "QtWindow") return view
|
||||||
|
else if (view is ViewGroup) {
|
||||||
|
for (i in 0 until view.childCount) {
|
||||||
|
val result = findQtWindow(view.getChildAt(i))
|
||||||
|
if (result != null) return result
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} else return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEvent(x: Float, y: Float, eventTime: Long, action: Int): MotionEvent =
|
||||||
|
MotionEvent.obtain(
|
||||||
|
eventTime,
|
||||||
|
eventTime,
|
||||||
|
action,
|
||||||
|
1,
|
||||||
|
arrayOf(MotionEvent.PointerProperties().apply {
|
||||||
|
id = 0
|
||||||
|
toolType = MotionEvent.TOOL_TYPE_FINGER
|
||||||
|
}),
|
||||||
|
arrayOf(MotionEvent.PointerCoords().apply {
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
pressure = 1f
|
||||||
|
size = 1f
|
||||||
|
}),
|
||||||
|
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
||||||
|
)
|
||||||
|
|
||||||
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
||||||
// also disable right-click, as it causes the application to crash
|
// also disable right-click, as it causes the application to crash
|
||||||
private var lastButtonState = 0
|
private var lastButtonState = 0
|
||||||
@@ -770,6 +896,7 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
|
Log.v(TAG, "dispatchTouch: $ev")
|
||||||
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||||
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
||||||
}
|
}
|
||||||
@@ -784,6 +911,13 @@ class AmneziaActivity : QtActivity() {
|
|||||||
/**
|
/**
|
||||||
* Utils methods
|
* Utils methods
|
||||||
*/
|
*/
|
||||||
|
private fun <T> blockingCall(
|
||||||
|
context: CoroutineContext = Dispatchers.Main.immediate,
|
||||||
|
block: suspend () -> T
|
||||||
|
) = runBlocking {
|
||||||
|
mainScope.async(context) { block() }.await()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun actionCodeToString(actionCode: Int): String =
|
private fun actionCodeToString(actionCode: Int): String =
|
||||||
when (actionCode) {
|
when (actionCode) {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package org.amnezia.vpn
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import org.amnezia.vpn.util.Log
|
||||||
|
|
||||||
|
private const val TAG = "TvFilePicker"
|
||||||
|
|
||||||
|
class TvFilePicker : ComponentActivity() {
|
||||||
|
|
||||||
|
private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||||
|
setResult(RESULT_OK, Intent().apply { data = it })
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Log.v(TAG, "onCreate")
|
||||||
|
getFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
Log.v(TAG, "onNewIntent")
|
||||||
|
getFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFile() {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "getFile")
|
||||||
|
fileChooseResultLauncher.launch("*/*")
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Log.w(TAG, "Activity not found")
|
||||||
|
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
||||||
|
finish()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get file: $e")
|
||||||
|
setResult(RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,9 +27,9 @@ if(WIN32)
|
|||||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
||||||
endif()
|
endif()
|
||||||
elseif(APPLE AND NOT IOS)
|
elseif(APPLE AND NOT IOS)
|
||||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/arm64_x86_64/libssh.a")
|
||||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/arm64_x86_64/libz.a")
|
||||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/arm64_x86_64")
|
||||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
||||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
||||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
||||||
|
|||||||
@@ -76,11 +76,7 @@ set_target_properties(${PROJECT} PROPERTIES
|
|||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
|
||||||
)
|
)
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
@@ -126,9 +122,9 @@ add_subdirectory(ios/networkextension)
|
|||||||
add_dependencies(${PROJECT} networkextension)
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
||||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
message("Client ==> MacOS NE build")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
|
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
|
|
||||||
|
enable_language(OBJC)
|
||||||
|
enable_language(Swift)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||||
|
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||||
|
|
||||||
|
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||||
|
find_library(FW_AVFOUNDATION AVFoundation)
|
||||||
|
find_library(FW_FOUNDATION Foundation)
|
||||||
|
find_library(FW_STOREKIT StoreKit)
|
||||||
|
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||||
|
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${FW_AUTHENTICATIONSERVICES}
|
||||||
|
${FW_AVFOUNDATION}
|
||||||
|
${FW_FOUNDATION}
|
||||||
|
${FW_STOREKIT}
|
||||||
|
${FW_USERNOTIFICATIONS}
|
||||||
|
${FW_NETWORKEXTENSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||||
|
)
|
||||||
|
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
|
||||||
|
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||||
|
)
|
||||||
|
|
||||||
|
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||||
|
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
|
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||||
|
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
|
||||||
|
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||||
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements"
|
||||||
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
XCODE_GENERATE_SCHEME TRUE
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
|
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
|
||||||
|
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
||||||
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
|
|
||||||
|
# XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development: TRAN VIET ANH (Y372SYT4WL)"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "org.amnezia.AmneziaVPNManual"
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
|
)
|
||||||
|
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
target_compile_options(${PROJECT} PRIVATE
|
||||||
|
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||||
|
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(macos/networkextension)
|
||||||
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
|
get_target_property(QtCore_location Qt6::Core LOCATION)
|
||||||
|
message("QtCore_location")
|
||||||
|
message(${QtCore_location})
|
||||||
|
|
||||||
|
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-macos/OpenVPNAdapter.framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-macos)
|
||||||
|
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-macos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ function(osx_bundle_assetcatalog TARGET)
|
|||||||
)
|
)
|
||||||
|
|
||||||
## Patch the asset catalog into the target bundle.
|
## Patch the asset catalog into the target bundle.
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(XCASSETS_RESOURCE_DIR "Resources")
|
set(XCASSETS_RESOURCE_DIR "Resources")
|
||||||
endif()
|
endif()
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||||
@@ -141,6 +141,7 @@ function(osx_codesign_target TARGET)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(FILE ${CODESIGN_FILES})
|
foreach(FILE ${CODESIGN_FILES})
|
||||||
|
message(STATUS "Signing ${TARGET}: ${FILE}")
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
||||||
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
||||||
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -120,7 +120,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
// 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");
|
||||||
#endif
|
#endif
|
||||||
@@ -129,7 +129,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// 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");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
|||||||
|
|
||||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
@@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEAD CODE.
|
||||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess *p = new QProcess();
|
QProcess *p = new QProcess();
|
||||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
|||||||
pathEnvVar.clear();
|
pathEnvVar.clear();
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MAC) && !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||||
{
|
{
|
||||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||||
if (l.isEmpty()) {
|
if (l.isEmpty()) {
|
||||||
|
|||||||
@@ -3,38 +3,169 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QUuid>
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Logger logger("XrayConfigurator");
|
||||||
|
}
|
||||||
|
|
||||||
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(settings, serverController, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
ErrorCode &errorCode)
|
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
// Generate new UUID for client
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
|
||||||
QString xrayPublicKey =
|
// Get current server config
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
QString currentConfig = m_serverController->getTextFileFromContainer(
|
||||||
xrayPublicKey.replace("\n", "");
|
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||||
|
|
||||||
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
|
|
||||||
xrayUuid.replace("\n", "");
|
|
||||||
|
|
||||||
QString xrayShortId =
|
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
|
||||||
xrayShortId.replace("\n", "");
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get server config file";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
config.replace("$XRAY_CLIENT_ID", xrayUuid);
|
// Parse current config as JSON
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
|
||||||
|
if (doc.isNull() || !doc.isObject()) {
|
||||||
|
logger.error() << "Failed to parse server config JSON";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject serverConfig = doc.object();
|
||||||
|
|
||||||
|
// Validate server config structure
|
||||||
|
if (!serverConfig.contains("inbounds")) {
|
||||||
|
logger.error() << "Server config missing 'inbounds' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray inbounds = serverConfig["inbounds"].toArray();
|
||||||
|
if (inbounds.isEmpty()) {
|
||||||
|
logger.error() << "Server config has empty 'inbounds' array";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject inbound = inbounds[0].toObject();
|
||||||
|
if (!inbound.contains("settings")) {
|
||||||
|
logger.error() << "Inbound missing 'settings' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject settings = inbound["settings"].toObject();
|
||||||
|
if (!settings.contains("clients")) {
|
||||||
|
logger.error() << "Settings missing 'clients' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray clients = settings["clients"].toArray();
|
||||||
|
|
||||||
|
// Create configuration for new client
|
||||||
|
QJsonObject clientConfig {
|
||||||
|
{"id", clientId},
|
||||||
|
{"flow", "xtls-rprx-vision"}
|
||||||
|
};
|
||||||
|
|
||||||
|
clients.append(clientConfig);
|
||||||
|
|
||||||
|
// Update config
|
||||||
|
settings["clients"] = clients;
|
||||||
|
inbound["settings"] = settings;
|
||||||
|
inbounds[0] = inbound;
|
||||||
|
serverConfig["inbounds"] = inbounds;
|
||||||
|
|
||||||
|
// Save updated config to server
|
||||||
|
QString updatedConfig = QJsonDocument(serverConfig).toJson();
|
||||||
|
errorCode = m_serverController->uploadTextFileToContainer(
|
||||||
|
container,
|
||||||
|
credentials,
|
||||||
|
updatedConfig,
|
||||||
|
amnezia::protocols::xray::serverConfigPath,
|
||||||
|
libssh::ScpOverwriteMode::ScpOverwriteExisting
|
||||||
|
);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload updated config";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart container
|
||||||
|
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
|
||||||
|
errorCode = m_serverController->runScript(
|
||||||
|
credentials,
|
||||||
|
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to restart container";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||||
|
{
|
||||||
|
// Get client ID from prepareServerConfig
|
||||||
|
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
||||||
|
logger.error() << "Failed to prepare server config";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
||||||
|
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
|
if (config.isEmpty()) {
|
||||||
|
logger.error() << "Failed to get config template";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xrayPublicKey =
|
||||||
|
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
||||||
|
logger.error() << "Failed to get public key";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
xrayPublicKey.replace("\n", "");
|
||||||
|
|
||||||
|
QString xrayShortId =
|
||||||
|
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
||||||
|
logger.error() << "Failed to get short ID";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
xrayShortId.replace("\n", "");
|
||||||
|
|
||||||
|
// Validate all required variables are present
|
||||||
|
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
|
||||||
|
logger.error() << "Config template missing required variables:"
|
||||||
|
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
|
||||||
|
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
|
||||||
|
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
||||||
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||||
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ public:
|
|||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||||
ErrorCode &errorCode);
|
ErrorCode &errorCode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||||
|
ErrorCode &errorCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAY_CONFIGURATOR_H
|
#endif // XRAY_CONFIGURATOR_H
|
||||||
|
|||||||
@@ -110,22 +110,19 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
|||||||
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
||||||
"own security protocol with SSL/TLS for key exchange.") },
|
"own security protocol with SSL/TLS for key exchange.") },
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
|
QObject::tr("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
|
||||||
"may be recognized by analysis systems in some highly censored regions.") },
|
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
||||||
"active-probing detection. Ideal for bypassing blocking in regions with the highest levels "
|
"active-probing detection. It is very resistant to detection, but offers low speed.") },
|
||||||
"of censorship.") },
|
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
|
||||||
"consumption. Recommended for regions with low levels of censorship.") },
|
"consumption.") },
|
||||||
{ DockerContainer::Awg,
|
{ DockerContainer::Awg,
|
||||||
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
|
QObject::tr("AmneziaWG is a special protocol from Amnezia based on WireGuard. "
|
||||||
"but very resistant to blockages. "
|
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.") },
|
||||||
"Recommended for regions with high levels of censorship.") },
|
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
|
QObject::tr("XRay with REALITY masks VPN traffic as web traffic and protects against active probing. "
|
||||||
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
|
"It is highly resistant to detection and offers high speed.") },
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
|
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
|
||||||
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
||||||
@@ -144,20 +141,20 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
|||||||
return {
|
return {
|
||||||
{ DockerContainer::OpenVpn,
|
{ DockerContainer::OpenVpn,
|
||||||
QObject::tr(
|
QObject::tr(
|
||||||
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
||||||
"It employs its unique security protocol, "
|
"It employs its unique security protocol, "
|
||||||
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
||||||
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
"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. "
|
"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, "
|
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
||||||
"which continually reinforces its security. "
|
"which continually reinforces its security. "
|
||||||
"With a strong balance of performance, security, and compatibility, "
|
"With a strong balance of performance, security, and compatibility, "
|
||||||
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
"* Normal power consumption on mobile devices\n"
|
"* Normal power consumption on mobile devices\n"
|
||||||
"* Flexible customisation to suit user needs to work with different operating systems and 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"
|
"* Recognised by DPI systems and therefore susceptible to blocking\n"
|
||||||
"* Can operate over both TCP and UDP network protocols.") },
|
"* Can operate over both TCP and UDP network protocols.") },
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
|
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."
|
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
|
||||||
@@ -169,28 +166,26 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
|||||||
"* Works over TCP network protocol.") },
|
"* Works over TCP network protocol.") },
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
||||||
"protecting against blocking.\n\n"
|
"protecting against detection.\n\n"
|
||||||
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
||||||
"and the server.\n\n"
|
"and the server.\n\n"
|
||||||
"Cloak protects OpenVPN from detection and blocking. \n\n"
|
"Cloak protects OpenVPN from detection. \n\n"
|
||||||
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
"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 "
|
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
||||||
"being detected\n\n"
|
"being detected\n\n"
|
||||||
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
|
"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 "
|
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
|
||||||
"invisible to analysis systems.\n\n"
|
"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"
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
"* High power consumption on mobile devices\n"
|
"* High power consumption on mobile devices\n"
|
||||||
"* Flexible settings\n"
|
"* Flexible settings\n"
|
||||||
"* Not recognised by DPI analysis systems\n"
|
"* Not recognised by detection systems\n"
|
||||||
"* Works over TCP network protocol, 443 port.\n") },
|
"* Works over TCP network protocol, 443 port.\n") },
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
||||||
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
||||||
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
"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. "
|
"WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. "
|
||||||
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
||||||
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
"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"
|
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
||||||
@@ -213,18 +208,18 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
|||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
"* Low power consumption\n"
|
"* Low power consumption\n"
|
||||||
"* Minimum number of settings\n"
|
"* Minimum number of settings\n"
|
||||||
"* Not recognised by DPI analysis systems, resistant to blocking\n"
|
"* Not recognised by traffic analysis systems\n"
|
||||||
"* Works over UDP network protocol.") },
|
"* Works over UDP network protocol.") },
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
||||||
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
|
"is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n"
|
||||||
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
|
"It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, "
|
||||||
"thus presenting an authentic TLS certificate and data. \n"
|
"thus presenting an authentic TLS certificate and data. \n"
|
||||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
||||||
"legitimate sites without the need for specific configurations. \n"
|
"legitimate sites without the need for specific configurations. \n"
|
||||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
||||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
|
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. "
|
||||||
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
|
"This makes REALITY a robust solution for maintaining internet freedom.")
|
||||||
},
|
},
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
||||||
@@ -280,7 +275,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
#elif defined(Q_OS_IOS)
|
#elif defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
@@ -332,9 +327,7 @@ QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
|
|||||||
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return true;
|
|
||||||
case DockerContainer::Awg: return true;
|
case DockerContainer::Awg: return true;
|
||||||
// case DockerContainer::Cloak: return true;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,9 +335,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
|||||||
QString ContainerProps::easySetupHeader(DockerContainer container)
|
QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return tr("Low");
|
case DockerContainer::Awg: return tr("Automatic");
|
||||||
case DockerContainer::Awg: return tr("High");
|
|
||||||
// case DockerContainer::Cloak: return tr("Extreme");
|
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,10 +343,8 @@ 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 my privacy.");
|
case DockerContainer::Awg: return tr("AmneziaWG protocol will be installed. "
|
||||||
case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
|
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.");
|
||||||
// case DockerContainer::Cloak:
|
|
||||||
// return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,9 +352,7 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
|||||||
int ContainerProps::easySetupOrder(DockerContainer container)
|
int ContainerProps::easySetupOrder(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::WireGuard: return 3;
|
case DockerContainer::Awg: return 1;
|
||||||
case DockerContainer::Awg: return 2;
|
|
||||||
// case DockerContainer::Cloak: return 1;
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,9 +371,9 @@ bool ContainerProps::isShareable(DockerContainer container)
|
|||||||
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
||||||
{
|
{
|
||||||
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
||||||
.toObject()
|
.toObject()
|
||||||
.value(config_key::last_config)
|
.value(config_key::last_config)
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ namespace
|
|||||||
constexpr char authData[] = "auth_data";
|
constexpr char authData[] = "auth_data";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
|
|
||||||
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
if (!sslErrors.empty()) {
|
if (!sslErrors.empty()) {
|
||||||
@@ -177,7 +179,7 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
|
|||||||
QStringList ApiController::getProxyUrls()
|
QStringList ApiController::getProxyUrls()
|
||||||
{
|
{
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(7000);
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
@@ -273,14 +275,14 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont
|
|||||||
|
|
||||||
void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig)
|
void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
IosController::Instance()->requestInetAccess();
|
IosController::Instance()->requestInetAccess();
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (serverConfig.value(config_key::configVersion).toInt()) {
|
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(7000);
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||||
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||||
@@ -330,13 +332,13 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c
|
|||||||
|
|
||||||
ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
IosController::Instance()->requestInetAccess();
|
IosController::Instance()->requestInetAccess();
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(7000);
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
|
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
|
||||||
@@ -391,13 +393,13 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
|||||||
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,
|
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,
|
||||||
QJsonObject &serverConfig)
|
QJsonObject &serverConfig)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
IosController::Instance()->requestInetAccess();
|
IosController::Instance()->requestInetAccess();
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(7000);
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
|
request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "configurators/openvpn_configurator.h"
|
#include "configurators/openvpn_configurator.h"
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,9 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::Awg) {
|
if (container == DockerContainer::Awg) {
|
||||||
if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
||||||
|
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
||||||
|
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
||||||
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
||||||
@@ -370,8 +372,10 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::WireGuard) {
|
if (container == DockerContainer::WireGuard) {
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))
|
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
||||||
|
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
||||||
|
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,6 +611,8 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||||||
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||||
|
|
||||||
// Amnezia wireguard vars
|
// Amnezia wireguard vars
|
||||||
|
vars.append({ { "$AWG_SUBNET_IP",
|
||||||
|
amneziaWireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
||||||
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
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_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -378,7 +378,7 @@ QString NetworkUtilities::getGatewayAndIface()
|
|||||||
close(sock);
|
close(sock);
|
||||||
return gateway_address;
|
return gateway_address;
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QString gateway;
|
QString gateway;
|
||||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||||
int afinet_type[] = {AF_INET, AF_INET6};
|
int afinet_type[] = {AF_INET, AF_INET6};
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ QJsonObject Deserialize(const QString &vmessStr, QString *alias, QString *errMes
|
|||||||
server.users.first().security = "auto";
|
server.users.first().security = "auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
const static auto getQueryValue = [&query](const QString &key, const QString &defaultValue) {
|
const auto getQueryValue = [&query](const QString &key, const QString &defaultValue) {
|
||||||
if (query.hasQueryItem(key))
|
if (query.hasQueryItem(key))
|
||||||
return query.queryItemValue(key, QUrl::FullyDecoded);
|
return query.queryItemValue(key, QUrl::FullyDecoded);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -114,12 +114,23 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
|||||||
|
|
||||||
// Bring up the wireguard interface if not already done.
|
// Bring up the wireguard interface if not already done.
|
||||||
if (!wgutils()->interfaceExists()) {
|
if (!wgutils()->interfaceExists()) {
|
||||||
|
// Create the interface.
|
||||||
if (!wgutils()->addInterface(config)) {
|
if (!wgutils()->addInterface(config)) {
|
||||||
logger.error() << "Interface creation failed.";
|
logger.error() << "Interface creation failed.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bring the interface up.
|
||||||
|
if (supportIPUtils()) {
|
||||||
|
if (!iputils()->addInterfaceIPs(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!iputils()->setMTUAndUp(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure routing for excluded addresses.
|
// Configure routing for excluded addresses.
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
addExclusionRoute(IPAddress(i));
|
addExclusionRoute(IPAddress(i));
|
||||||
@@ -135,15 +146,6 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportIPUtils()) {
|
|
||||||
if (!iputils()->addInterfaceIPs(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!iputils()->setMTUAndUp(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set routing
|
// set routing
|
||||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
|
#include "daemonerrors.h"
|
||||||
#include "dnsutils.h"
|
#include "dnsutils.h"
|
||||||
#include "interfaceconfig.h"
|
#include "interfaceconfig.h"
|
||||||
#include "iputils.h"
|
#include "iputils.h"
|
||||||
@@ -51,7 +53,7 @@ class Daemon : public QObject {
|
|||||||
*/
|
*/
|
||||||
void activationFailure();
|
void activationFailure();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure();
|
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class DaemonError : uint8_t {
|
||||||
|
ERROR_NONE = 0u,
|
||||||
|
ERROR_FATAL = 1u,
|
||||||
|
ERROR_SPLIT_TUNNEL_INIT_FAILURE = 2u,
|
||||||
|
ERROR_SPLIT_TUNNEL_START_FAILURE = 3u,
|
||||||
|
ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE = 4u,
|
||||||
|
|
||||||
|
DAEMON_ERROR_MAX = 5u,
|
||||||
|
};
|
||||||
@@ -159,9 +159,10 @@ void DaemonLocalServerConnection::disconnected() {
|
|||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonLocalServerConnection::backendFailure() {
|
void DaemonLocalServerConnection::backendFailure(DaemonError err) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj.insert("type", "backendFailure");
|
obj.insert("type", "backendFailure");
|
||||||
|
obj.insert("errorCode", static_cast<int>(err));
|
||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "daemonerrors.h"
|
||||||
|
|
||||||
class QLocalSocket;
|
class QLocalSocket;
|
||||||
|
|
||||||
class DaemonLocalServerConnection final : public QObject {
|
class DaemonLocalServerConnection final : public QObject {
|
||||||
@@ -23,7 +25,7 @@ class DaemonLocalServerConnection final : public QObject {
|
|||||||
|
|
||||||
void connected(const QString& pubkey);
|
void connected(const QString& pubkey);
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure();
|
void backendFailure(DaemonError err);
|
||||||
|
|
||||||
void write(const QJsonObject& obj);
|
void write(const QJsonObject& obj);
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ class WireguardUtils : public QObject {
|
|||||||
|
|
||||||
virtual bool updateRoutePrefix(const IPAddress& prefix) = 0;
|
virtual bool updateRoutePrefix(const IPAddress& prefix) = 0;
|
||||||
virtual bool deleteRoutePrefix(const IPAddress& prefix) = 0;
|
virtual bool deleteRoutePrefix(const IPAddress& prefix) = 0;
|
||||||
|
|
||||||
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
|
|
||||||
|
virtual bool excludeLocalNetworks(const QList<IPAddress>& addresses) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARDUTILS_H
|
#endif // WIREGUARDUTILS_H
|
||||||
|
|||||||
@@ -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="M18 13V19C18 19.5304 17.7893 20.0391 17.4142 20.4142C17.0391 20.7893 16.5304 21 16 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V8C3 7.46957 3.21071 6.96086 3.58579 6.58579C3.96086 6.21071 4.46957 6 5 6H11" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M15 3H21V9" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10 14L21 3" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 644 B |
@@ -27,12 +27,7 @@ set_target_properties(networkextension PROPERTIES
|
|||||||
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension"
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
XCODEBUILD="/usr/bin/xcodebuild"
|
|
||||||
WORKINGDIR=`pwd`
|
|
||||||
PATCH="/usr/bin/patch"
|
|
||||||
|
|
||||||
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
|
||||||
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
|
||||||
PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
|
|
||||||
CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
|
||||||
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
|
||||||
EOF
|
|
||||||
|
|
||||||
|
|
||||||
cd 3rd/OpenVPNAdapter
|
|
||||||
if $XCODEBUILD -scheme OpenVPNAdapter -configuration Release -xcconfig Configuration/amnezia.xcconfig -sdk iphoneos -destination 'generic/platform=iOS' -project OpenVPNAdapter.xcodeproj ; then
|
|
||||||
echo "OpenVPNAdapter built successfully"
|
|
||||||
else
|
|
||||||
echo "OpenVPNAdapter build failed"
|
|
||||||
fi
|
|
||||||
cd ../../
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 682 B |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -1,6 +1,68 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"images": [
|
||||||
"author" : "xcode",
|
{
|
||||||
"version" : 1
|
"idiom": "mac",
|
||||||
|
"size": "16x16",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "16.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "16x16",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "16@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "32x32",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "32.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "32x32",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "32@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "128x128",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "128.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "128x128",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "128@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "256x256",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "256.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "256x256",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "256@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "512x512",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "512.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "512x512",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "512@2x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"version": 1,
|
||||||
|
"author": "xcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleAllowMixedLocalizations</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.utilities</string>
|
||||||
|
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>CFBundleIcons</key>
|
||||||
|
<dict/>
|
||||||
|
<key>UTImportedTypeDeclarations</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>vpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>WireGuard config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>conf</string>
|
||||||
|
<string>cfg</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>OpenVPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>ovpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>AmneziaVPN backup file</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>backup</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSExtensions</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.networkextension.packet-tunnel</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.application-identifier</key>
|
|
||||||
<string>$(DEVELOPMENT_TEAM).$(APP_ID_MACOS)</string>
|
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
@@ -15,15 +12,12 @@
|
|||||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
<string>$(DEVELOPMENT_TEAM).*</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>$(DEVELOPMENT_TEAM)</string>
|
|
||||||
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
@@ -31,5 +25,10 @@
|
|||||||
|
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)group.org.amnezia.AmneziaVPN</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -3,40 +3,56 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.application-identifier</key>
|
<key>com.apple.application-identifier</key>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
|
<string>X7UJ388FXK.org.amnezia.AmneziaVPN.network-extension</string>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
<key>com.apple.developer.team-identifier</key>
|
||||||
<string>$(DEVELOPMENT_TEAM)</string>
|
<string>X7UJ388FXK</string>
|
||||||
|
|
||||||
<key>com.apple.developer.system-extension.install</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.security.assets.movies.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.assets.music.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.assets.pictures.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.bluetooth</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.camera</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.usb</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.downloads.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<!-- <key>com.apple.security.networkextension</key>
|
||||||
|
<true/> -->
|
||||||
|
<key>com.apple.security.personal-information.addressbook</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.private.network.socket-delegate</key>
|
<key>com.apple.security.personal-information.calendars</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.personal-information.location</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.print</key>
|
||||||
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)org.amnezia.AmneziaVPN.network-extension</string>
|
||||||
|
</array>
|
||||||
|
<!-- <key>com.apple.security.network.extension</key>
|
||||||
|
<true/> -->
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
enable_language(Swift)
|
||||||
|
message("Client message >> macos build >> networkextension")
|
||||||
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
|
||||||
|
add_executable(networkextension)
|
||||||
|
|
||||||
|
if(MACOS_NE)
|
||||||
|
message("MACOS_NE is ON")
|
||||||
|
add_definitions(-DQ_OS_MAC)
|
||||||
|
add_definitions(-DMACOS_NE)
|
||||||
|
else()
|
||||||
|
message("MACOS_NE is OFF")
|
||||||
|
endif()
|
||||||
|
message("executable_path is: @executable_path/../../Frameworks")
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
|
||||||
|
BUNDLE_EXTENSION appex
|
||||||
|
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPNNetworkExtension"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
|
||||||
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension"
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES"
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
|
|
||||||
|
# XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore network-extension"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "amnezia.AmneziaVPN.network-extensionManual"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development: TRAN VIET ANH (Y372SYT4WL)"
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties("networkextension" PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
|
||||||
|
find_library(FW_MOBILE_CORE MobileCoreServices)
|
||||||
|
find_library(FW_UI_KIT UIKit)
|
||||||
|
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
||||||
|
|
||||||
|
|
||||||
|
# Set the root directory
|
||||||
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
|
||||||
|
set(CMAKE_FRAMEWORK_PATH ${CLIENT_ROOT_DIR}/3rd/OpenVPNAdapter/build/Release-macos)
|
||||||
|
|
||||||
|
target_link_libraries(networkextension PRIVATE ${FW_LIBRESOLV})
|
||||||
|
|
||||||
|
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||||
|
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
||||||
|
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
|
||||||
|
|
||||||
|
message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}")
|
||||||
|
message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}")
|
||||||
|
|
||||||
|
target_sources(networkextension PRIVATE
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(networkextension PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET networkextension APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
## Build wireguard-go-version.h
|
||||||
|
execute_process(
|
||||||
|
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||||
|
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
||||||
|
OUTPUT_VARIABLE WG_VERSION_FULL
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||||
|
target_sources(networkextension PRIVATE
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||||
|
|
||||||
|
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
|
||||||
|
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/arm64_x86_64/libwg-go.a)
|
||||||
|
|
||||||
|
message(${CLIENT_ROOT_DIR})
|
||||||
|
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||||
|
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||||
|
|
||||||
|
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
||||||
@@ -3,27 +3,32 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>AmneziaVPNNetworkExtension</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
|
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>org.amnezia.AmneziaVPN.network-extension</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>${APPLE_PROJECT_VERSION}</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>${CMAKE_PROJECT_VERSION_TWEAK}</string>
|
||||||
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
|
||||||
|
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
|
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
@@ -31,5 +36,11 @@
|
|||||||
<key>NSExtensionPrincipalClass</key>
|
<key>NSExtensionPrincipalClass</key>
|
||||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
|
||||||
|
<key>com.wireguard.macos.app_group_id</key>
|
||||||
|
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>1C8F.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>C617.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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 "macos/gobridge/wireguard.h"
|
|
||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||||
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -23,3 +23,8 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex);
|
|||||||
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
||||||
|
|
||||||
void write_msg_to_log(const char* tag, const char* msg);
|
void write_msg_to_log(const char* tag, const char* msg);
|
||||||
|
|
||||||
|
// init function definition in C
|
||||||
|
void hev_socks5_tunnel_quit(void);
|
||||||
|
// Updated function definition in C
|
||||||
|
int hev_socks5_tunnel_main(const char* configFile, int fd);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#ifndef WIREGUARD_GO_VERSION
|
||||||
|
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||||
|
#endif // WIREGUARD_GO_VERSION
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
bool isAnotherInstanceRunning()
|
bool isAnotherInstanceRunning()
|
||||||
{
|
{
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (isAnotherInstanceRunning()) {
|
if (isAnotherInstanceRunning()) {
|
||||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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 "protocols/protocols_defs.h"
|
|
||||||
#include "localsocketcontroller.h"
|
#include "localsocketcontroller.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
@@ -17,6 +18,9 @@
|
|||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "models/server.h"
|
#include "models/server.h"
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
|
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
// How many times do we try to reconnect.
|
// How many times do we try to reconnect.
|
||||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||||
@@ -451,8 +455,39 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == "backendFailure") {
|
if (type == "backendFailure") {
|
||||||
qCritical() << "backendFailure";
|
if (!obj.contains("errorCode")) {
|
||||||
return;
|
// report a generic error if we dont know what it is.
|
||||||
|
logger.error() << "generic backend failure error";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto errorCode = static_cast<uint8_t>(obj["errorCode"].toInt());
|
||||||
|
if (errorCode >= (uint8_t)DaemonError::DAEMON_ERROR_MAX) {
|
||||||
|
// Also report a generic error if the code is invalid.
|
||||||
|
logger.error() << "invalid backend failure error code";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (static_cast<DaemonError>(errorCode)) {
|
||||||
|
case DaemonError::ERROR_NONE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_FATAL:
|
||||||
|
logger.error() << "generic backend failure error (fatal or error none)";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
|
break;
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_INIT_FAILURE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE:
|
||||||
|
logger.error() << "split tunnel backend failure error";
|
||||||
|
//REPORTERROR(ErrorHandler::SplitTunnelError, "controller");
|
||||||
|
break;
|
||||||
|
case DaemonError::DAEMON_ERROR_MAX:
|
||||||
|
// We should not get here.
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == "logs") {
|
if (type == "logs") {
|
||||||
|
|||||||
@@ -163,9 +163,7 @@ QString AndroidController::openFile(const QString &filter)
|
|||||||
QString fileName;
|
QString fileName;
|
||||||
connect(this, &AndroidController::fileOpened, this,
|
connect(this, &AndroidController::fileOpened, this,
|
||||||
[&fileName, &wait](const QString &uri) {
|
[&fileName, &wait](const QString &uri) {
|
||||||
qDebug() << "Android event: file opened; uri:" << uri;
|
fileName = uri;
|
||||||
fileName = QQmlFile::urlToLocalFileOrQrc(uri);
|
|
||||||
qDebug() << "Android opened filename:" << fileName;
|
|
||||||
wait.quit();
|
wait.quit();
|
||||||
},
|
},
|
||||||
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||||
@@ -175,6 +173,25 @@ QString AndroidController::openFile(const QString &filter)
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AndroidController::getFd(const QString &fileName)
|
||||||
|
{
|
||||||
|
return callActivityMethod<jint>("getFd", "(Ljava/lang/String;)I",
|
||||||
|
QJniObject::fromString(fileName).object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidController::closeFd()
|
||||||
|
{
|
||||||
|
callActivityMethod("closeFd", "()V");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidController::getFileName(const QString &uri)
|
||||||
|
{
|
||||||
|
auto fileName = callActivityMethod<jstring, jstring>("getFileName", "(Ljava/lang/String;)Ljava/lang/String;",
|
||||||
|
QJniObject::fromString(uri).object<jstring>());
|
||||||
|
QJniEnvironment env;
|
||||||
|
return AndroidUtils::convertJString(env.jniEnv(), fileName.object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
bool AndroidController::isCameraPresent()
|
bool AndroidController::isCameraPresent()
|
||||||
{
|
{
|
||||||
return callActivityMethod<jboolean>("isCameraPresent", "()Z");
|
return callActivityMethod<jboolean>("isCameraPresent", "()Z");
|
||||||
@@ -287,6 +304,11 @@ bool AndroidController::requestAuthentication()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidController::sendTouch(float x, float y)
|
||||||
|
{
|
||||||
|
callActivityMethod("sendTouch", "(FF)V", x, y);
|
||||||
|
}
|
||||||
|
|
||||||
// Moving log processing to the Android side
|
// Moving log processing to the Android side
|
||||||
jclass AndroidController::log;
|
jclass AndroidController::log;
|
||||||
jmethodID AndroidController::logDebug;
|
jmethodID AndroidController::logDebug;
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public:
|
|||||||
void resetLastServer(int serverIndex);
|
void resetLastServer(int serverIndex);
|
||||||
void saveFile(const QString &fileName, const QString &data);
|
void saveFile(const QString &fileName, const QString &data);
|
||||||
QString openFile(const QString &filter);
|
QString openFile(const QString &filter);
|
||||||
|
int getFd(const QString &fileName);
|
||||||
|
void closeFd();
|
||||||
|
QString getFileName(const QString &uri);
|
||||||
bool isCameraPresent();
|
bool isCameraPresent();
|
||||||
bool isOnTv();
|
bool isOnTv();
|
||||||
void startQrReaderActivity();
|
void startQrReaderActivity();
|
||||||
@@ -48,6 +51,7 @@ public:
|
|||||||
bool isNotificationPermissionGranted();
|
bool isNotificationPermissionGranted();
|
||||||
void requestNotificationPermission();
|
void requestNotificationPermission();
|
||||||
bool requestAuthentication();
|
bool requestAuthentication();
|
||||||
|
void sendTouch(float x, float y);
|
||||||
|
|
||||||
static bool initLogging();
|
static bool initLogging();
|
||||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import HevSocks5Tunnel
|
import HevSocks5Tunnel
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
public enum Socks5Tunnel {
|
public enum Socks5Tunnel {
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
|||||||
startHandler = completionHandler
|
startHandler = completionHandler
|
||||||
ovpnAdapter?.connect(using: packetFlow)
|
ovpnAdapter?.connect(using: packetFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
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
|
||||||
|
|||||||
@@ -112,9 +112,19 @@ extension PacketTunnelProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastHandshakeString = settingsDictionary["last_handshake_time_sec"]
|
||||||
|
let lastHandshake: Int64
|
||||||
|
|
||||||
|
if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) {
|
||||||
|
lastHandshake = handshakeValue
|
||||||
|
} else {
|
||||||
|
lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec`
|
||||||
|
}
|
||||||
|
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0",
|
||||||
|
"last_handshake_time_sec": lastHandshake
|
||||||
]
|
]
|
||||||
|
|
||||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if !MACOS_NE
|
||||||
#include "QRCodeReaderBase.h"
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
@@ -108,3 +109,19 @@ void QRCodeReader::startReading() {
|
|||||||
void QRCodeReader::stopReading() {
|
void QRCodeReader::stopReading() {
|
||||||
[m_qrCodeReader stopReading];
|
[m_qrCodeReader stopReading];
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
|
QRCodeReader::QRCodeReader()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect QRCodeReader::cameraSize() {
|
||||||
|
return QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRCodeReader::startReading() {}
|
||||||
|
void QRCodeReader::stopReading() {}
|
||||||
|
void QRCodeReader::setCameraSize(QRect) {}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
#if !MACOS_NE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
@interface QIOSApplicationDelegate
|
@interface QIOSApplicationDelegate
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
||||||
|
#if !MACOS_NE
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
||||||
@@ -57,5 +57,5 @@
|
|||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
#if MACOS_NE
|
||||||
|
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScreenProtection {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#else
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||||
@@ -85,3 +95,4 @@ struct ProtectionPair {
|
|||||||
textField.removeFromSuperview()
|
textField.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
void disconnectVpn();
|
void disconnectVpn();
|
||||||
|
|
||||||
void vpnStatusDidChange(void *pNotification);
|
void vpnStatusDidChange(void *pNotification);
|
||||||
|
|
||||||
void vpnConfigurationDidChange(void *pNotification);
|
void vpnConfigurationDidChange(void *pNotification);
|
||||||
|
|
||||||
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const char* MessageKey::isOnDemand = "is-on-demand";
|
|||||||
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||||
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
static UIViewController* getViewController() {
|
static UIViewController* getViewController() {
|
||||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||||
for (UIWindow *window in windows) {
|
for (UIWindow *window in windows) {
|
||||||
@@ -36,6 +37,7 @@ static UIViewController* getViewController() {
|
|||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -249,6 +251,19 @@ void IosController::checkStatus()
|
|||||||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||||
|
|
||||||
|
uint64_t last_handshake_time_sec = 0;
|
||||||
|
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
|
||||||
|
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
|
||||||
|
} else {
|
||||||
|
qDebug() << "Key last_handshake_time_sec is missing or null";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_handshake_time_sec < 0) {
|
||||||
|
disconnectVpn();
|
||||||
|
qDebug() << "Invalid handshake time, disconnecting VPN.";
|
||||||
|
}
|
||||||
|
|
||||||
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
||||||
m_rxBytes = rxBytes;
|
m_rxBytes = rxBytes;
|
||||||
m_txBytes = txBytes;
|
m_txBytes = txBytes;
|
||||||
@@ -789,14 +804,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||||
[sharingItems addObject:logFileUrl];
|
[sharingItems addObject:logFileUrl];
|
||||||
}
|
}
|
||||||
|
#if !MACOS_NE
|
||||||
UIViewController *qtController = getViewController();
|
UIViewController *qtController = getViewController();
|
||||||
if (!qtController) return;
|
if (!qtController) return;
|
||||||
|
|
||||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||||
|
#endif
|
||||||
__block bool isAccepted = false;
|
__block bool isAccepted = false;
|
||||||
|
#if !MACOS_NE
|
||||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||||
isAccepted = completed;
|
isAccepted = completed;
|
||||||
emit finished();
|
emit finished();
|
||||||
@@ -808,7 +823,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
popController.sourceView = qtController.view;
|
popController.sourceView = qtController.view;
|
||||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
@@ -817,6 +832,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString IosController::openFile() {
|
QString IosController::openFile() {
|
||||||
|
#if !MACOS_NE
|
||||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||||
|
|
||||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||||
@@ -826,9 +842,9 @@ QString IosController::openFile() {
|
|||||||
if (!qtController) return;
|
if (!qtController) return;
|
||||||
|
|
||||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||||
|
#endif
|
||||||
__block QString filePath;
|
__block QString filePath;
|
||||||
|
#if !MACOS_NE
|
||||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||||
if (path) {
|
if (path) {
|
||||||
filePath = QString::fromUtf8(path.UTF8String);
|
filePath = QString::fromUtf8(path.UTF8String);
|
||||||
@@ -837,7 +853,7 @@ QString IosController::openFile() {
|
|||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
#import <NetworkExtension/NetworkExtension.h>
|
#import <NetworkExtension/NetworkExtension.h>
|
||||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
#include <UIKit/UIKit.h>
|
#include <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
|
|
||||||
class IosController;
|
class IosController;
|
||||||
@@ -17,9 +21,10 @@ class IosController;
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||||
|
#if !MACOS_NE
|
||||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||||
|
|
||||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DocumentPickerDelegate
|
#if !MACOS_NE
|
||||||
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
for (NSURL *url in urls) {
|
for (NSURL *url in urls) {
|
||||||
@@ -42,4 +43,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#import <UserNotifications/UserNotifications.h>
|
#import <UserNotifications/UserNotifications.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface IOSNotificationDelegate
|
@interface IOSNotificationDelegate
|
||||||
@@ -87,3 +89,86 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS
|
||||||
|
@interface IOSNotificationDelegate
|
||||||
|
: NSObject <UNUserNotificationCenterDelegate> {
|
||||||
|
IOSNotificationHandler* m_iosNotificationHandler;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation IOSNotificationDelegate
|
||||||
|
|
||||||
|
- (id)initWithObject:(IOSNotificationHandler*)notification {
|
||||||
|
self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific
|
||||||
|
if (self) {
|
||||||
|
m_iosNotificationHandler = notification;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||||
|
willPresentNotification:(UNNotification*)notification
|
||||||
|
withCompletionHandler:
|
||||||
|
(void (^)(UNNotificationPresentationOptions options))completionHandler {
|
||||||
|
Q_UNUSED(center)
|
||||||
|
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||||
|
didReceiveNotificationResponse:(UNNotificationResponse*)response
|
||||||
|
withCompletionHandler:(void (^)())completionHandler {
|
||||||
|
Q_UNUSED(center)
|
||||||
|
Q_UNUSED(response)
|
||||||
|
completionHandler();
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {
|
||||||
|
|
||||||
|
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||||
|
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
|
||||||
|
UNAuthorizationOptionBadge)
|
||||||
|
completionHandler:^(BOOL granted, NSError* _Nullable error) {
|
||||||
|
Q_UNUSED(granted);
|
||||||
|
if (!error) {
|
||||||
|
m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
IOSNotificationHandler::~IOSNotificationHandler() { }
|
||||||
|
|
||||||
|
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
||||||
|
const QString& message, int timerMsec) {
|
||||||
|
Q_UNUSED(type);
|
||||||
|
|
||||||
|
if (!m_delegate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
||||||
|
content.title = title.toNSString();
|
||||||
|
content.body = message.toNSString();
|
||||||
|
content.sound = [UNNotificationSound defaultSound];
|
||||||
|
|
||||||
|
int timerSec = timerMsec / 1000;
|
||||||
|
UNTimeIntervalNotificationTrigger* trigger =
|
||||||
|
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||||
|
|
||||||
|
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||||
|
content:content
|
||||||
|
trigger:trigger];
|
||||||
|
|
||||||
|
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||||
|
center.delegate = (id<UNUserNotificationCenterDelegate>)m_delegate;
|
||||||
|
|
||||||
|
[center addNotificationRequest:request
|
||||||
|
withCompletionHandler:^(NSError* _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"Local Notification failed");
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ QStringList LinuxFirewall::getDNSRules(const QStringList& servers)
|
|||||||
result << QStringLiteral("-o amn0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
result << QStringLiteral("-o amn0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||||
result << QStringLiteral("-o tun0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
result << QStringLiteral("-o tun0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||||
result << QStringLiteral("-o tun0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
result << QStringLiteral("-o tun0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||||
|
result << QStringLiteral("-o tun2+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||||
|
result << QStringLiteral("-o tun2+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -277,6 +279,7 @@ void LinuxFirewall::install()
|
|||||||
installAnchor(Both, QStringLiteral("200.allowVPN"), {
|
installAnchor(Both, QStringLiteral("200.allowVPN"), {
|
||||||
QStringLiteral("-o amn0+ -j ACCEPT"),
|
QStringLiteral("-o amn0+ -j ACCEPT"),
|
||||||
QStringLiteral("-o tun0+ -j ACCEPT"),
|
QStringLiteral("-o tun0+ -j ACCEPT"),
|
||||||
|
QStringLiteral("-o tun2+ -j ACCEPT"),
|
||||||
});
|
});
|
||||||
|
|
||||||
installAnchor(IPv4, QStringLiteral("120.blockNets"), {});
|
installAnchor(IPv4, QStringLiteral("120.blockNets"), {});
|
||||||
|
|||||||
@@ -297,31 +297,6 @@ QList<WireguardUtils::PeerStatus> WireguardUtilsLinux::getPeerStatus() {
|
|||||||
return peerList;
|
return peerList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
|
|
||||||
{
|
|
||||||
// double-check + ensure our firewall is installed and enabled
|
|
||||||
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
|
|
||||||
|
|
||||||
// Note: rule precedence is handled inside IpTablesFirewall
|
|
||||||
LinuxFirewall::ensureRootAnchorPriority();
|
|
||||||
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
|
|
||||||
LinuxFirewall::updateAllowNets(params.allowAddrs);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
|
|
||||||
LinuxFirewall::updateBlockNets(params.blockAddrs);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
|
||||||
LinuxFirewall::updateDNSServers(params.dnsServers);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
@@ -377,6 +352,26 @@ bool WireguardUtilsLinux::deleteExclusionRoute(const IPAddress& prefix) {
|
|||||||
return m_rtmonitor->deleteExclusionRoute(prefix);
|
return m_rtmonitor->deleteExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsLinux::excludeLocalNetworks(const QList<IPAddress>& routes) {
|
||||||
|
if (!m_rtmonitor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly discard LAN traffic that makes its way into the tunnel. This
|
||||||
|
// doesn't really exclude the LAN traffic, we just don't take any action to
|
||||||
|
// overrule the routes of other interfaces.
|
||||||
|
bool result = true;
|
||||||
|
for (const auto& prefix : routes) {
|
||||||
|
logger.error() << "Attempting to exclude:" << prefix.toString();
|
||||||
|
if (!m_rtmonitor->insertRoute(prefix)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: A kill switch would be nice though :)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QString WireguardUtilsLinux::uapiCommand(const QString& command) {
|
QString WireguardUtilsLinux::uapiCommand(const QString& command) {
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
QTimer uapiTimeout;
|
QTimer uapiTimeout;
|
||||||
@@ -450,3 +445,27 @@ QString WireguardUtilsLinux::waitForTunnelName(const QString& filename) {
|
|||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
|
||||||
|
{
|
||||||
|
// double-check + ensure our firewall is installed and enabled
|
||||||
|
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
|
||||||
|
|
||||||
|
// Note: rule precedence is handled inside IpTablesFirewall
|
||||||
|
LinuxFirewall::ensureRootAnchorPriority();
|
||||||
|
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
|
||||||
|
LinuxFirewall::updateAllowNets(params.allowAddrs);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
|
||||||
|
LinuxFirewall::updateBlockNets(params.blockAddrs);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
||||||
|
LinuxFirewall::updateDNSServers(params.dnsServers);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ public:
|
|||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix) override;
|
bool addExclusionRoute(const IPAddress& prefix) override;
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
||||||
|
|
||||||
|
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||||
|
|
||||||
void applyFirewallRules(FirewallParams& params);
|
void applyFirewallRules(FirewallParams& params);
|
||||||
signals:
|
signals:
|
||||||
void backendFailure();
|
void backendFailure();
|
||||||
|
|||||||
@@ -358,8 +358,8 @@ void MacosRouteMonitor::rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
||||||
unsigned int ifindex,
|
unsigned int ifindex, const void* gateway,
|
||||||
const void* gateway) {
|
int flags) {
|
||||||
constexpr size_t rtm_max_size = sizeof(struct rt_msghdr) +
|
constexpr size_t rtm_max_size = sizeof(struct rt_msghdr) +
|
||||||
sizeof(struct sockaddr_in6) * 2 +
|
sizeof(struct sockaddr_in6) * 2 +
|
||||||
sizeof(struct sockaddr_storage);
|
sizeof(struct sockaddr_storage);
|
||||||
@@ -370,7 +370,7 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
|||||||
rtm->rtm_version = RTM_VERSION;
|
rtm->rtm_version = RTM_VERSION;
|
||||||
rtm->rtm_type = action;
|
rtm->rtm_type = action;
|
||||||
rtm->rtm_index = ifindex;
|
rtm->rtm_index = ifindex;
|
||||||
rtm->rtm_flags = RTF_STATIC | RTF_UP;
|
rtm->rtm_flags = flags | RTF_STATIC | RTF_UP;
|
||||||
rtm->rtm_addrs = 0;
|
rtm->rtm_addrs = 0;
|
||||||
rtm->rtm_pid = 0;
|
rtm->rtm_pid = 0;
|
||||||
rtm->rtm_seq = m_rtseq++;
|
rtm->rtm_seq = m_rtseq++;
|
||||||
@@ -490,7 +490,7 @@ bool MacosRouteMonitor::rtmFetchRoutes(int family) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::insertRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::insertRoute(const IPAddress& prefix, int flags) {
|
||||||
struct sockaddr_dl datalink;
|
struct sockaddr_dl datalink;
|
||||||
memset(&datalink, 0, sizeof(datalink));
|
memset(&datalink, 0, sizeof(datalink));
|
||||||
datalink.sdl_family = AF_LINK;
|
datalink.sdl_family = AF_LINK;
|
||||||
@@ -502,11 +502,11 @@ bool MacosRouteMonitor::insertRoute(const IPAddress& prefix) {
|
|||||||
datalink.sdl_slen = 0;
|
datalink.sdl_slen = 0;
|
||||||
memcpy(&datalink.sdl_data, qPrintable(m_ifname), datalink.sdl_nlen);
|
memcpy(&datalink.sdl_data, qPrintable(m_ifname), datalink.sdl_nlen);
|
||||||
|
|
||||||
return rtmSendRoute(RTM_ADD, prefix, m_ifindex, &datalink);
|
return rtmSendRoute(RTM_ADD, prefix, m_ifindex, &datalink, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) {
|
||||||
return rtmSendRoute(RTM_DELETE, prefix, m_ifindex, nullptr);
|
return rtmSendRoute(RTM_DELETE, prefix, m_ifindex, nullptr, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ class MacosRouteMonitor final : public QObject {
|
|||||||
MacosRouteMonitor(const QString& ifname, QObject* parent = nullptr);
|
MacosRouteMonitor(const QString& ifname, QObject* parent = nullptr);
|
||||||
~MacosRouteMonitor();
|
~MacosRouteMonitor();
|
||||||
|
|
||||||
bool insertRoute(const IPAddress& prefix);
|
bool insertRoute(const IPAddress& prefix, int flags = 0);
|
||||||
bool deleteRoute(const IPAddress& prefix);
|
bool deleteRoute(const IPAddress& prefix, int flags = 0);
|
||||||
int interfaceFlags() { return m_ifflags; }
|
int interfaceFlags() { return m_ifflags; }
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix);
|
bool addExclusionRoute(const IPAddress& prefix);
|
||||||
@@ -37,7 +37,7 @@ class MacosRouteMonitor final : public QObject {
|
|||||||
void handleRtmUpdate(const struct rt_msghdr* msg, const QByteArray& payload);
|
void handleRtmUpdate(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||||
void handleIfaceInfo(const struct if_msghdr* msg, const QByteArray& payload);
|
void handleIfaceInfo(const struct if_msghdr* msg, const QByteArray& payload);
|
||||||
bool rtmSendRoute(int action, const IPAddress& prefix, unsigned int ifindex,
|
bool rtmSendRoute(int action, const IPAddress& prefix, unsigned int ifindex,
|
||||||
const void* gateway);
|
const void* gateway, int flags = 0);
|
||||||
bool rtmFetchRoutes(int family);
|
bool rtmFetchRoutes(int family);
|
||||||
static void rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen, int rtaddr,
|
static void rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen, int rtaddr,
|
||||||
const void* sa);
|
const void* sa);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "wireguardutilsmacos.h"
|
#include "wireguardutilsmacos.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -130,7 +131,6 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
} else {
|
} else {
|
||||||
@@ -211,7 +211,6 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
|||||||
logger.warning() << "Failed to create peer with no endpoints";
|
logger.warning() << "Failed to create peer with no endpoints";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out << config.m_serverPort << "\n";
|
out << config.m_serverPort << "\n";
|
||||||
|
|
||||||
out << "replace_allowed_ips=true\n";
|
out << "replace_allowed_ips=true\n";
|
||||||
@@ -323,10 +322,10 @@ bool WireguardUtilsMacos::deleteRoutePrefix(const IPAddress& prefix) {
|
|||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (prefix.prefixLength() > 0) {
|
|
||||||
return m_rtmonitor->insertRoute(prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (prefix.prefixLength() > 0) {
|
||||||
|
return m_rtmonitor->deleteRoute(prefix);
|
||||||
|
}
|
||||||
// Ensure that we do not replace the default route.
|
// Ensure that we do not replace the default route.
|
||||||
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
||||||
return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) &&
|
return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) &&
|
||||||
@@ -346,31 +345,6 @@ bool WireguardUtilsMacos::addExclusionRoute(const IPAddress& prefix) {
|
|||||||
return m_rtmonitor->addExclusionRoute(prefix);
|
return m_rtmonitor->addExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WireguardUtilsMacos::applyFirewallRules(FirewallParams& params)
|
|
||||||
{
|
|
||||||
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
|
||||||
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
|
||||||
if (!MacOSFirewall::isInstalled()) MacOSFirewall::install();
|
|
||||||
|
|
||||||
MacOSFirewall::ensureRootAnchorPriority();
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), params.blockAll);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), params.allowNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), params.allowNets,
|
|
||||||
QStringLiteral("allownets"), params.allowAddrs);
|
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), params.blockNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), params.blockNets,
|
|
||||||
QStringLiteral("blocknets"), params.blockAddrs);
|
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), params.dnsServers);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
@@ -378,6 +352,26 @@ bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
|||||||
return m_rtmonitor->deleteExclusionRoute(prefix);
|
return m_rtmonitor->deleteExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsMacos::excludeLocalNetworks(const QList<IPAddress>& routes) {
|
||||||
|
if (!m_rtmonitor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly discard LAN traffic that makes its way into the tunnel. This
|
||||||
|
// doesn't really exclude the LAN traffic, we just don't take any action to
|
||||||
|
// overrule the routes of other interfaces.
|
||||||
|
bool result = true;
|
||||||
|
for (const auto& prefix : routes) {
|
||||||
|
logger.error() << "Attempting to exclude:" << prefix.toString();
|
||||||
|
if (!m_rtmonitor->insertRoute(prefix, RTF_IFSCOPE | RTF_REJECT)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: A kill switch would be nice though :)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QString WireguardUtilsMacos::uapiCommand(const QString& command) {
|
QString WireguardUtilsMacos::uapiCommand(const QString& command) {
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
QTimer uapiTimeout;
|
QTimer uapiTimeout;
|
||||||
@@ -454,3 +448,28 @@ QString WireguardUtilsMacos::waitForTunnelName(const QString& filename) {
|
|||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WireguardUtilsMacos::applyFirewallRules(FirewallParams& params)
|
||||||
|
{
|
||||||
|
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||||
|
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||||
|
if (!MacOSFirewall::isInstalled()) MacOSFirewall::install();
|
||||||
|
|
||||||
|
MacOSFirewall::ensureRootAnchorPriority();
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), params.blockAll);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), params.allowNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), params.allowNets,
|
||||||
|
QStringLiteral("allownets"), params.allowAddrs);
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), params.blockNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), params.blockNets,
|
||||||
|
QStringLiteral("blocknets"), params.blockAddrs);
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), params.dnsServers);
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class WireguardUtilsMacos final : public WireguardUtils {
|
|||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix) override;
|
bool addExclusionRoute(const IPAddress& prefix) override;
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
||||||
|
|
||||||
|
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||||
|
|
||||||
void applyFirewallRules(FirewallParams& params);
|
void applyFirewallRules(FirewallParams& params);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "windowsdaemon.h"
|
#include "windowsdaemon.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <qassert.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@@ -15,28 +16,34 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
#include "dnsutilswindows.h"
|
#include "dnsutilswindows.h"
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "platforms/windows/daemon/windowsfirewall.h"
|
||||||
|
#include "platforms/windows/daemon/windowssplittunnel.h"
|
||||||
#include "platforms/windows/windowscommons.h"
|
#include "platforms/windows/windowscommons.h"
|
||||||
#include "platforms/windows/windowsservicemanager.h"
|
|
||||||
#include "windowsfirewall.h"
|
#include "windowsfirewall.h"
|
||||||
|
|
||||||
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("WindowsDaemon");
|
Logger logger("WindowsDaemon");
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsDaemon::WindowsDaemon() : Daemon(nullptr), m_splitTunnelManager(this) {
|
WindowsDaemon::WindowsDaemon() : Daemon(nullptr) {
|
||||||
MZ_COUNT_CTOR(WindowsDaemon);
|
MZ_COUNT_CTOR(WindowsDaemon);
|
||||||
|
m_firewallManager = WindowsFirewall::create(this);
|
||||||
|
Q_ASSERT(m_firewallManager != nullptr);
|
||||||
|
|
||||||
m_wgutils = new WireguardUtilsWindows(this);
|
m_wgutils = WireguardUtilsWindows::create(m_firewallManager, this);
|
||||||
m_dnsutils = new DnsUtilsWindows(this);
|
m_dnsutils = new DnsUtilsWindows(this);
|
||||||
|
m_splitTunnelManager = WindowsSplitTunnel::create(m_firewallManager);
|
||||||
|
|
||||||
connect(m_wgutils, &WireguardUtilsWindows::backendFailure, this,
|
connect(m_wgutils.get(), &WireguardUtilsWindows::backendFailure, this,
|
||||||
&WindowsDaemon::monitorBackendFailure);
|
&WindowsDaemon::monitorBackendFailure);
|
||||||
connect(this, &WindowsDaemon::activationFailure,
|
connect(this, &WindowsDaemon::activationFailure,
|
||||||
[]() { WindowsFirewall::instance()->disableKillSwitch(); });
|
[this]() { m_firewallManager->disableKillSwitch(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsDaemon::~WindowsDaemon() {
|
WindowsDaemon::~WindowsDaemon() {
|
||||||
@@ -57,28 +64,42 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
|
|||||||
|
|
||||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||||
if (config.m_vpnDisabledApps.length() > 0) {
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
m_splitTunnelManager.start(m_inetAdapterIndex, vpnAdapterIndex);
|
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
||||||
m_splitTunnelManager.setRules(config.m_vpnDisabledApps);
|
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
||||||
} else {
|
} else {
|
||||||
m_splitTunnelManager.stop();
|
m_splitTunnelManager->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
||||||
if (op == Down) {
|
if (!m_splitTunnelManager) {
|
||||||
m_splitTunnelManager.stop();
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
|
// The Client has sent us a list of disabled apps, but we failed
|
||||||
|
// to init the the split tunnel driver.
|
||||||
|
// So let the client know this was not possible
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_INIT_FAILURE);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op == Up) {
|
if (op == Down) {
|
||||||
logger.debug() << "Tunnel UP, Starting SplitTunneling";
|
m_splitTunnelManager->stop();
|
||||||
if (!WindowsSplitTunnel::isInstalled()) {
|
return true;
|
||||||
logger.warning() << "Split Tunnel Driver not Installed yet, fixing this.";
|
|
||||||
WindowsSplitTunnel::installDriver();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
activateSplitTunnel(config);
|
if (!m_splitTunnelManager->start(m_inetAdapterIndex)) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||||
|
};
|
||||||
|
if (!m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps)) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE);
|
||||||
|
};
|
||||||
|
// Now the driver should be running (State == 4)
|
||||||
|
if (!m_splitTunnelManager->isRunning()) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_splitTunnelManager->stop();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
#ifndef WINDOWSDAEMON_H
|
#ifndef WINDOWSDAEMON_H
|
||||||
#define WINDOWSDAEMON_H
|
#define WINDOWSDAEMON_H
|
||||||
|
|
||||||
|
#include <qpointer.h>
|
||||||
|
|
||||||
#include "daemon/daemon.h"
|
#include "daemon/daemon.h"
|
||||||
#include "dnsutilswindows.h"
|
#include "dnsutilswindows.h"
|
||||||
|
#include "windowsfirewall.h"
|
||||||
#include "windowssplittunnel.h"
|
#include "windowssplittunnel.h"
|
||||||
#include "windowstunnelservice.h"
|
#include "windowstunnelservice.h"
|
||||||
#include "wireguardutilswindows.h"
|
#include "wireguardutilswindows.h"
|
||||||
@@ -25,7 +28,7 @@ class WindowsDaemon final : public Daemon {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool run(Op op, const InterfaceConfig& config) override;
|
bool run(Op op, const InterfaceConfig& config) override;
|
||||||
WireguardUtils* wgutils() const override { return m_wgutils; }
|
WireguardUtils* wgutils() const override { return m_wgutils.get(); }
|
||||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -39,9 +42,10 @@ class WindowsDaemon final : public Daemon {
|
|||||||
|
|
||||||
int m_inetAdapterIndex = -1;
|
int m_inetAdapterIndex = -1;
|
||||||
|
|
||||||
WireguardUtilsWindows* m_wgutils = nullptr;
|
std::unique_ptr<WireguardUtilsWindows> m_wgutils;
|
||||||
DnsUtilsWindows* m_dnsutils = nullptr;
|
DnsUtilsWindows* m_dnsutils = nullptr;
|
||||||
WindowsSplitTunnel m_splitTunnelManager;
|
std::unique_ptr<WindowsSplitTunnel> m_splitTunnelManager;
|
||||||
|
QPointer<WindowsFirewall> m_firewallManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WINDOWSDAEMON_H
|
#endif // WINDOWSDAEMON_H
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
#include <guiddef.h>
|
#include <guiddef.h>
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include <netfw.h>
|
#include <netfw.h>
|
||||||
//#include <qaccessible.h>
|
#include <qaccessible.h>
|
||||||
#include <Ws2tcpip.h>
|
#include <qassert.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <Ws2tcpip.h>
|
||||||
|
#include "winsock.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -27,7 +28,6 @@
|
|||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "platforms/windows/windowsutils.h"
|
#include "platforms/windows/windowsutils.h"
|
||||||
#include "winsock.h"
|
|
||||||
|
|
||||||
#define IPV6_ADDRESS_SIZE 16
|
#define IPV6_ADDRESS_SIZE 16
|
||||||
|
|
||||||
@@ -49,18 +49,13 @@ constexpr uint8_t HIGH_WEIGHT = 13;
|
|||||||
constexpr uint8_t MAX_WEIGHT = 15;
|
constexpr uint8_t MAX_WEIGHT = 15;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
WindowsFirewall* WindowsFirewall::instance() {
|
WindowsFirewall* WindowsFirewall::create(QObject* parent) {
|
||||||
if (s_instance == nullptr) {
|
if (s_instance != nullptr) {
|
||||||
s_instance = new WindowsFirewall(qApp);
|
// Only one instance of the firewall is allowed
|
||||||
|
// Q_ASSERT(false);
|
||||||
|
return s_instance;
|
||||||
}
|
}
|
||||||
return s_instance;
|
HANDLE engineHandle = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) {
|
|
||||||
MZ_COUNT_CTOR(WindowsFirewall);
|
|
||||||
Q_ASSERT(s_instance == nullptr);
|
|
||||||
|
|
||||||
HANDLE engineHandle = NULL;
|
|
||||||
DWORD result = ERROR_SUCCESS;
|
DWORD result = ERROR_SUCCESS;
|
||||||
// Use dynamic sessions for efficiency and safety:
|
// Use dynamic sessions for efficiency and safety:
|
||||||
// -> Filtering policy objects are deleted even when the application crashes/
|
// -> Filtering policy objects are deleted even when the application crashes/
|
||||||
@@ -71,15 +66,24 @@ WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) {
|
|||||||
|
|
||||||
logger.debug() << "Opening the filter engine.";
|
logger.debug() << "Opening the filter engine.";
|
||||||
|
|
||||||
result =
|
result = FwpmEngineOpen0(nullptr, RPC_C_AUTHN_WINNT, nullptr, &session,
|
||||||
FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle);
|
&engineHandle);
|
||||||
|
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
WindowsUtils::windowsLog("FwpmEngineOpen0 failed");
|
WindowsUtils::windowsLog("FwpmEngineOpen0 failed");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
logger.debug() << "Filter engine opened successfully.";
|
logger.debug() << "Filter engine opened successfully.";
|
||||||
m_sessionHandle = engineHandle;
|
if (!initSublayer()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
s_instance = new WindowsFirewall(engineHandle, parent);
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsFirewall::WindowsFirewall(HANDLE session, QObject* parent)
|
||||||
|
: QObject(parent), m_sessionHandle(session) {
|
||||||
|
MZ_COUNT_CTOR(WindowsFirewall);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsFirewall::~WindowsFirewall() {
|
WindowsFirewall::~WindowsFirewall() {
|
||||||
@@ -89,15 +93,8 @@ WindowsFirewall::~WindowsFirewall() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::init() {
|
// static
|
||||||
if (m_init) {
|
bool WindowsFirewall::initSublayer() {
|
||||||
logger.warning() << "Alread initialised FW_WFP layer";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (m_sessionHandle == INVALID_HANDLE_VALUE) {
|
|
||||||
logger.error() << "Cant Init Sublayer with invalid wfp handle";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we were not able to aquire a handle, this will fail anyway.
|
// If we were not able to aquire a handle, this will fail anyway.
|
||||||
// We need to open up another handle because of wfp rules:
|
// We need to open up another handle because of wfp rules:
|
||||||
// If a wfp resource was created with SESSION_DYNAMIC,
|
// If a wfp resource was created with SESSION_DYNAMIC,
|
||||||
@@ -157,11 +154,10 @@ bool WindowsFirewall::init() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Initialised Sublayer";
|
logger.debug() << "Initialised Sublayer";
|
||||||
m_init = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||||
// Checks if the FW_Rule was enabled succesfully,
|
// Checks if the FW_Rule was enabled succesfully,
|
||||||
// disables the whole killswitch and returns false if not.
|
// disables the whole killswitch and returns false if not.
|
||||||
#define FW_OK(rule) \
|
#define FW_OK(rule) \
|
||||||
@@ -184,7 +180,7 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
|
||||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||||
"Allow usage of VPN Adapter"));
|
"Allow usage of VPN Adapter"));
|
||||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||||
@@ -200,6 +196,36 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
|||||||
#undef FW_OK
|
#undef FW_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow unprotected traffic sent to the following local address ranges.
|
||||||
|
bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||||
|
// Start the firewall transaction
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
disableKillSwitch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
disableKillSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Blocking unprotected traffic
|
||||||
|
for (const IPAddress& prefix : ranges) {
|
||||||
|
if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup.dismiss();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||||
// Start the firewall transaction
|
// Start the firewall transaction
|
||||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
@@ -238,10 +264,10 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
|||||||
|
|
||||||
if (!config.m_excludedAddresses.empty()) {
|
if (!config.m_excludedAddresses.empty()) {
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
logger.debug() << "range: " << i;
|
logger.debug() << "excludedAddresses range: " << i;
|
||||||
|
|
||||||
if (!allowTrafficToRange(i, HIGH_WEIGHT,
|
if (!allowTrafficTo(i, HIGH_WEIGHT,
|
||||||
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,9 +447,59 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight,
|
||||||
|
const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
GUID layerKeyOut;
|
||||||
|
GUID layerKeyIn;
|
||||||
|
if (addr.type() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
layerKeyOut = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
layerKeyIn = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
} else {
|
||||||
|
layerKeyOut = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
layerKeyIn = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the IP address range.
|
||||||
|
FWPM_FILTER_CONDITION0 cond[1] = {};
|
||||||
|
FWP_RANGE0 ipRange;
|
||||||
|
QByteArray lowIpV6Buffer;
|
||||||
|
QByteArray highIpV6Buffer;
|
||||||
|
|
||||||
|
importAddress(addr.address(), ipRange.valueLow, &lowIpV6Buffer);
|
||||||
|
importAddress(addr.broadcastAddress(), 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.filterCondition = cond;
|
||||||
|
|
||||||
|
// Send the filters down to the firewall.
|
||||||
|
QString description = "Permit traffic %1 " + addr.toString();
|
||||||
|
filter.layerKey = layerKeyOut;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("to"), peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = layerKeyIn;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("from"), peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -484,57 +560,6 @@ 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
|
||||||
{
|
{
|
||||||
@@ -734,7 +759,7 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
|||||||
filter.weight.uint8 = weight;
|
filter.weight.uint8 = weight;
|
||||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
FWPM_FILTER_CONDITION0 cond[1] = {0};
|
FWPM_FILTER_CONDITION0 cond[1] = {};
|
||||||
FWP_RANGE0 ipRange;
|
FWP_RANGE0 ipRange;
|
||||||
QByteArray lowIpV6Buffer;
|
QByteArray lowIpV6Buffer;
|
||||||
QByteArray highIpV6Buffer;
|
QByteArray highIpV6Buffer;
|
||||||
|
|||||||
@@ -26,18 +26,27 @@ struct FWP_CONDITION_VALUE0_;
|
|||||||
|
|
||||||
class WindowsFirewall final : public QObject {
|
class WindowsFirewall final : public QObject {
|
||||||
public:
|
public:
|
||||||
~WindowsFirewall();
|
/**
|
||||||
|
* @brief Opens the Windows Filtering Platform, initializes the session,
|
||||||
|
* sublayer. Returns a WindowsFirewall object if successful, otherwise
|
||||||
|
* nullptr. If there is already a WindowsFirewall object, it will be returned.
|
||||||
|
*
|
||||||
|
* @param parent - parent QObject
|
||||||
|
* @return WindowsFirewall* - nullptr if failed to open the Windows Filtering
|
||||||
|
* Platform.
|
||||||
|
*/
|
||||||
|
static WindowsFirewall* create(QObject* parent);
|
||||||
|
~WindowsFirewall() override;
|
||||||
|
|
||||||
static WindowsFirewall* instance();
|
bool enableInterface(int vpnAdapterIndex);
|
||||||
bool init();
|
bool enableLanBypass(const QList<IPAddress>& ranges);
|
||||||
|
|
||||||
bool enableKillSwitch(int vpnAdapterIndex);
|
|
||||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||||
bool disablePeerTraffic(const QString& pubkey);
|
bool disablePeerTraffic(const QString& pubkey);
|
||||||
bool disableKillSwitch();
|
bool disableKillSwitch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WindowsFirewall(QObject* parent);
|
static bool initSublayer();
|
||||||
|
WindowsFirewall(HANDLE session, QObject* parent);
|
||||||
HANDLE m_sessionHandle;
|
HANDLE m_sessionHandle;
|
||||||
bool m_init = false;
|
bool m_init = false;
|
||||||
QList<uint64_t> m_activeRules;
|
QList<uint64_t> m_activeRules;
|
||||||
@@ -50,11 +59,10 @@ class WindowsFirewall final : public QObject {
|
|||||||
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||||
const QString& title, const QString& peer = QString());
|
const QString& title, const QString& peer = QString());
|
||||||
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
||||||
|
bool allowTrafficTo(const IPAddress& addr, int weight, const QString& title,
|
||||||
|
const QString& peer = QString());
|
||||||
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);
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ namespace {
|
|||||||
Logger logger("WindowsRouteMonitor");
|
Logger logger("WindowsRouteMonitor");
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
|
// Attempt to mark routing entries that we create with a relatively
|
||||||
|
// high metric. This ensures that we can skip over routes of our own
|
||||||
|
// creation when processing route changes, and ensures that we give
|
||||||
|
// way to other routing entries.
|
||||||
|
constexpr const ULONG EXCLUSION_ROUTE_METRIC = 0x5e72;
|
||||||
|
|
||||||
// Called by the kernel on route changes - perform some basic filtering and
|
// Called by the kernel on route changes - perform some basic filtering and
|
||||||
// invoke the routeChanged slot to do the real work.
|
// invoke the routeChanged slot to do the real work.
|
||||||
static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
||||||
@@ -20,22 +26,17 @@ static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
|||||||
WindowsRouteMonitor* monitor = (WindowsRouteMonitor*)context;
|
WindowsRouteMonitor* monitor = (WindowsRouteMonitor*)context;
|
||||||
Q_UNUSED(type);
|
Q_UNUSED(type);
|
||||||
|
|
||||||
// Ignore host route changes, and unsupported protocols.
|
// Ignore route changes that we created.
|
||||||
if (row->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
if ((row->Protocol == MIB_IPPROTO_NETMGMT) &&
|
||||||
if (row->DestinationPrefix.PrefixLength >= 128) {
|
(row->Metric == EXCLUSION_ROUTE_METRIC)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (row->DestinationPrefix.Prefix.si_family == AF_INET) {
|
if (monitor->getLuid() == row->InterfaceLuid.Value) {
|
||||||
if (row->DestinationPrefix.PrefixLength >= 32) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitor->getLuid() != row->InterfaceLuid.Value) {
|
// Invoke the route changed signal to do the real work in Qt.
|
||||||
QMetaObject::invokeMethod(monitor, "routeChanged", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(monitor, "routeChanged", Qt::QueuedConnection);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform prefix matching comparison on IP addresses in host order.
|
// Perform prefix matching comparison on IP addresses in host order.
|
||||||
@@ -57,7 +58,8 @@ static int prefixcmp(const void* a, const void* b, size_t bits) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsRouteMonitor::WindowsRouteMonitor(QObject* parent) : QObject(parent) {
|
WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent)
|
||||||
|
: QObject(parent), m_luid(luid) {
|
||||||
MZ_COUNT_CTOR(WindowsRouteMonitor);
|
MZ_COUNT_CTOR(WindowsRouteMonitor);
|
||||||
logger.debug() << "WindowsRouteMonitor created.";
|
logger.debug() << "WindowsRouteMonitor created.";
|
||||||
|
|
||||||
@@ -67,11 +69,13 @@ WindowsRouteMonitor::WindowsRouteMonitor(QObject* parent) : QObject(parent) {
|
|||||||
WindowsRouteMonitor::~WindowsRouteMonitor() {
|
WindowsRouteMonitor::~WindowsRouteMonitor() {
|
||||||
MZ_COUNT_DTOR(WindowsRouteMonitor);
|
MZ_COUNT_DTOR(WindowsRouteMonitor);
|
||||||
CancelMibChangeNotify2(m_routeHandle);
|
CancelMibChangeNotify2(m_routeHandle);
|
||||||
flushExclusionRoutes();
|
|
||||||
|
flushRouteTable(m_exclusionRoutes);
|
||||||
|
flushRouteTable(m_clonedRoutes);
|
||||||
logger.debug() << "WindowsRouteMonitor destroyed.";
|
logger.debug() << "WindowsRouteMonitor destroyed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
void WindowsRouteMonitor::updateInterfaceMetrics(int family) {
|
||||||
PMIB_IPINTERFACE_TABLE table;
|
PMIB_IPINTERFACE_TABLE table;
|
||||||
DWORD result = GetIpInterfaceTable(family, &table);
|
DWORD result = GetIpInterfaceTable(family, &table);
|
||||||
if (result != NO_ERROR) {
|
if (result != NO_ERROR) {
|
||||||
@@ -82,10 +86,10 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
|||||||
|
|
||||||
// Flush the list of interfaces that are valid for routing.
|
// Flush the list of interfaces that are valid for routing.
|
||||||
if ((family == AF_INET) || (family == AF_UNSPEC)) {
|
if ((family == AF_INET) || (family == AF_UNSPEC)) {
|
||||||
m_validInterfacesIpv4.clear();
|
m_interfaceMetricsIpv4.clear();
|
||||||
}
|
}
|
||||||
if ((family == AF_INET6) || (family == AF_UNSPEC)) {
|
if ((family == AF_INET6) || (family == AF_UNSPEC)) {
|
||||||
m_validInterfacesIpv6.clear();
|
m_interfaceMetricsIpv6.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild the list of interfaces that are valid for routing.
|
// Rebuild the list of interfaces that are valid for routing.
|
||||||
@@ -101,12 +105,12 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
|||||||
if (row->Family == AF_INET) {
|
if (row->Family == AF_INET) {
|
||||||
logger.debug() << "Interface" << row->InterfaceIndex
|
logger.debug() << "Interface" << row->InterfaceIndex
|
||||||
<< "is valid for IPv4 routing";
|
<< "is valid for IPv4 routing";
|
||||||
m_validInterfacesIpv4.append(row->InterfaceLuid.Value);
|
m_interfaceMetricsIpv4[row->InterfaceLuid.Value] = row->Metric;
|
||||||
}
|
}
|
||||||
if (row->Family == AF_INET6) {
|
if (row->Family == AF_INET6) {
|
||||||
logger.debug() << "Interface" << row->InterfaceIndex
|
logger.debug() << "Interface" << row->InterfaceIndex
|
||||||
<< "is valid for IPv6 routing";
|
<< "is valid for IPv6 routing";
|
||||||
m_validInterfacesIpv6.append(row->InterfaceLuid.Value);
|
m_interfaceMetricsIpv6[row->InterfaceLuid.Value] = row->Metric;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,72 +130,72 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
|
|||||||
if (row->InterfaceLuid.Value == m_luid) {
|
if (row->InterfaceLuid.Value == m_luid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ignore host routes, and shorter potential matches.
|
if (row->DestinationPrefix.PrefixLength < bestMatch) {
|
||||||
if (row->DestinationPrefix.PrefixLength >=
|
|
||||||
data->DestinationPrefix.PrefixLength) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (row->DestinationPrefix.PrefixLength < bestMatch) {
|
// Ignore routes of our own creation.
|
||||||
|
if ((row->Protocol == data->Protocol) && (row->Metric == data->Metric)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the routing table entry matches the destination.
|
// Check if the routing table entry matches the destination.
|
||||||
|
if (!routeContainsDest(&row->DestinationPrefix, &data->DestinationPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the combined interface and routing metric.
|
||||||
|
ULONG routeMetric = row->Metric;
|
||||||
if (data->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
if (data->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
||||||
if (row->DestinationPrefix.Prefix.Ipv6.sin6_family != AF_INET6) {
|
if (!m_interfaceMetricsIpv6.contains(row->InterfaceLuid.Value)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m_validInterfacesIpv6.contains(row->InterfaceLuid.Value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prefixcmp(&data->DestinationPrefix.Prefix.Ipv6.sin6_addr,
|
|
||||||
&row->DestinationPrefix.Prefix.Ipv6.sin6_addr,
|
|
||||||
row->DestinationPrefix.PrefixLength) != 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
routeMetric += m_interfaceMetricsIpv6[row->InterfaceLuid.Value];
|
||||||
} else if (data->DestinationPrefix.Prefix.si_family == AF_INET) {
|
} else if (data->DestinationPrefix.Prefix.si_family == AF_INET) {
|
||||||
if (row->DestinationPrefix.Prefix.Ipv4.sin_family != AF_INET) {
|
if (!m_interfaceMetricsIpv4.contains(row->InterfaceLuid.Value)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m_validInterfacesIpv4.contains(row->InterfaceLuid.Value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prefixcmp(&data->DestinationPrefix.Prefix.Ipv4.sin_addr,
|
|
||||||
&row->DestinationPrefix.Prefix.Ipv4.sin_addr,
|
|
||||||
row->DestinationPrefix.PrefixLength) != 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
routeMetric += m_interfaceMetricsIpv4[row->InterfaceLuid.Value];
|
||||||
} else {
|
} else {
|
||||||
// Unsupported destination address family.
|
// Unsupported destination address family.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (routeMetric < row->Metric) {
|
||||||
|
routeMetric = ULONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer routes with lower metric if we find multiple matches
|
// Prefer routes with lower metric if we find multiple matches
|
||||||
// with the same prefix length.
|
// with the same prefix length.
|
||||||
if ((row->DestinationPrefix.PrefixLength == bestMatch) &&
|
if ((row->DestinationPrefix.PrefixLength == bestMatch) &&
|
||||||
(row->Metric >= bestMetric)) {
|
(routeMetric >= bestMetric)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got here, then this is the longest prefix match so far.
|
// If we got here, then this is the longest prefix match so far.
|
||||||
memcpy(&nexthop, &row->NextHop, sizeof(SOCKADDR_INET));
|
memcpy(&nexthop, &row->NextHop, sizeof(SOCKADDR_INET));
|
||||||
bestLuid = row->InterfaceLuid.Value;
|
|
||||||
bestMatch = row->DestinationPrefix.PrefixLength;
|
bestMatch = row->DestinationPrefix.PrefixLength;
|
||||||
bestMetric = row->Metric;
|
bestMetric = routeMetric;
|
||||||
|
if (bestMatch == data->DestinationPrefix.PrefixLength) {
|
||||||
|
bestLuid = 0; // Don't write to the table if we find an exact match.
|
||||||
|
} else {
|
||||||
|
bestLuid = row->InterfaceLuid.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If neither the interface nor next-hop have changed, then do nothing.
|
// If neither the interface nor next-hop have changed, then do nothing.
|
||||||
if ((data->InterfaceLuid.Value) == bestLuid &&
|
if (data->InterfaceLuid.Value == bestLuid &&
|
||||||
memcmp(&nexthop, &data->NextHop, sizeof(SOCKADDR_INET)) == 0) {
|
memcmp(&nexthop, &data->NextHop, sizeof(SOCKADDR_INET)) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the routing table entry.
|
// Delete the previous routing table entry, if any.
|
||||||
if (data->InterfaceLuid.Value != 0) {
|
if (data->InterfaceLuid.Value != 0) {
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
||||||
logger.error() << "Failed to delete route:" << result;
|
logger.error() << "Failed to delete route:" << result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the routing table entry.
|
||||||
data->InterfaceLuid.Value = bestLuid;
|
data->InterfaceLuid.Value = bestLuid;
|
||||||
memcpy(&data->NextHop, &nexthop, sizeof(SOCKADDR_INET));
|
memcpy(&data->NextHop, &nexthop, sizeof(SOCKADDR_INET));
|
||||||
if (data->InterfaceLuid.Value != 0) {
|
if (data->InterfaceLuid.Value != 0) {
|
||||||
@@ -202,10 +206,178 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool WindowsRouteMonitor::routeContainsDest(const IP_ADDRESS_PREFIX* route,
|
||||||
|
const IP_ADDRESS_PREFIX* dest) {
|
||||||
|
if (route->Prefix.si_family != dest->Prefix.si_family) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (route->PrefixLength > dest->PrefixLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (route->Prefix.si_family == AF_INET) {
|
||||||
|
return prefixcmp(&route->Prefix.Ipv4.sin_addr, &dest->Prefix.Ipv4.sin_addr,
|
||||||
|
route->PrefixLength) == 0;
|
||||||
|
} else if (route->Prefix.si_family == AF_INET6) {
|
||||||
|
return prefixcmp(&route->Prefix.Ipv6.sin6_addr,
|
||||||
|
&dest->Prefix.Ipv6.sin6_addr, route->PrefixLength) == 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QHostAddress WindowsRouteMonitor::prefixToAddress(
|
||||||
|
const IP_ADDRESS_PREFIX* dest) {
|
||||||
|
if (dest->Prefix.si_family == AF_INET6) {
|
||||||
|
return QHostAddress(dest->Prefix.Ipv6.sin6_addr.s6_addr);
|
||||||
|
} else if (dest->Prefix.si_family == AF_INET) {
|
||||||
|
quint32 addr = htonl(dest->Prefix.Ipv4.sin_addr.s_addr);
|
||||||
|
return QHostAddress(addr);
|
||||||
|
} else {
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsRouteMonitor::isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const {
|
||||||
|
auto i = m_exclusionRoutes.constBegin();
|
||||||
|
while (i != m_exclusionRoutes.constEnd()) {
|
||||||
|
const MIB_IPFORWARD_ROW2* row = i.value();
|
||||||
|
if (routeContainsDest(&row->DestinationPrefix, dest)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::updateCapturedRoutes(int family) {
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PMIB_IPFORWARD_TABLE2 table;
|
||||||
|
DWORD error = GetIpForwardTable2(family, &table);
|
||||||
|
if (error != NO_ERROR) {
|
||||||
|
updateCapturedRoutes(family, table);
|
||||||
|
FreeMibTable(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||||
|
PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable);
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ULONG i = 0; i < table->NumEntries; i++) {
|
||||||
|
MIB_IPFORWARD_ROW2* row = &table->Table[i];
|
||||||
|
// Ignore routes into the VPN interface.
|
||||||
|
if (row->InterfaceLuid.Value == m_luid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore the default route
|
||||||
|
if (row->DestinationPrefix.PrefixLength == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore routes of our own creation.
|
||||||
|
if ((row->Protocol == MIB_IPPROTO_NETMGMT) &&
|
||||||
|
(row->Metric == EXCLUSION_ROUTE_METRIC)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore routes which should be excluded.
|
||||||
|
if (isRouteExcluded(&row->DestinationPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QHostAddress destination = prefixToAddress(&row->DestinationPrefix);
|
||||||
|
if (destination.isLoopback() || destination.isBroadcast() ||
|
||||||
|
destination.isLinkLocal() || destination.isMulticast()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, this route should be cloned.
|
||||||
|
IPAddress prefix(destination, row->DestinationPrefix.PrefixLength);
|
||||||
|
MIB_IPFORWARD_ROW2* data = m_clonedRoutes.value(prefix, nullptr);
|
||||||
|
if (data != nullptr) {
|
||||||
|
// Count the number of matching entries in the main table.
|
||||||
|
data->Age++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.debug() << "Capturing route to"
|
||||||
|
<< logger.sensitive(prefix.toString());
|
||||||
|
|
||||||
|
// Clone the route and direct it into the VPN tunnel.
|
||||||
|
data = new MIB_IPFORWARD_ROW2;
|
||||||
|
InitializeIpForwardEntry(data);
|
||||||
|
data->InterfaceLuid.Value = m_luid;
|
||||||
|
data->DestinationPrefix = row->DestinationPrefix;
|
||||||
|
data->NextHop.si_family = data->DestinationPrefix.Prefix.si_family;
|
||||||
|
|
||||||
|
// Set the rest of the flags for a static route.
|
||||||
|
data->ValidLifetime = 0xffffffff;
|
||||||
|
data->PreferredLifetime = 0xffffffff;
|
||||||
|
data->Metric = 0;
|
||||||
|
data->Protocol = MIB_IPPROTO_NETMGMT;
|
||||||
|
data->Loopback = false;
|
||||||
|
data->AutoconfigureAddress = false;
|
||||||
|
data->Publish = false;
|
||||||
|
data->Immortal = false;
|
||||||
|
data->Age = 0;
|
||||||
|
|
||||||
|
// Route this traffic into the VPN tunnel.
|
||||||
|
DWORD result = CreateIpForwardEntry2(data);
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
logger.error() << "Failed to update route:" << result;
|
||||||
|
delete data;
|
||||||
|
} else {
|
||||||
|
m_clonedRoutes.insert(prefix, data);
|
||||||
|
data->Age++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally scan for any routes which were removed from the table. We do this
|
||||||
|
// by reusing the age field to count the number of matching entries in the
|
||||||
|
// main table.
|
||||||
|
auto i = m_clonedRoutes.begin();
|
||||||
|
while (i != m_clonedRoutes.end()) {
|
||||||
|
MIB_IPFORWARD_ROW2* data = i.value();
|
||||||
|
if (data->Age > 0) {
|
||||||
|
// Entry is in use, don't delete it.
|
||||||
|
data->Age = 0;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((family != AF_UNSPEC) &&
|
||||||
|
(data->DestinationPrefix.Prefix.si_family != family)) {
|
||||||
|
// We are not processing updates to this address family.
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug() << "Removing route capture for"
|
||||||
|
<< logger.sensitive(i.key().toString());
|
||||||
|
|
||||||
|
// Otherwise, this route is no longer in use.
|
||||||
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
|
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
||||||
|
logger.error() << "Failed to delete route:" << result;
|
||||||
|
}
|
||||||
|
delete data;
|
||||||
|
i = m_clonedRoutes.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Adding exclusion route for"
|
logger.debug() << "Adding exclusion route for"
|
||||||
<< logger.sensitive(prefix.toString());
|
<< logger.sensitive(prefix.toString());
|
||||||
|
|
||||||
|
// Silently ignore non-routeable addresses.
|
||||||
|
QHostAddress addr = prefix.address();
|
||||||
|
if (addr.isLoopback() || addr.isBroadcast() || addr.isLinkLocal() ||
|
||||||
|
addr.isMulticast()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_exclusionRoutes.contains(prefix)) {
|
if (m_exclusionRoutes.contains(prefix)) {
|
||||||
logger.warning() << "Exclusion route already exists";
|
logger.warning() << "Exclusion route already exists";
|
||||||
return false;
|
return false;
|
||||||
@@ -232,7 +404,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
|||||||
// Set the rest of the flags for a static route.
|
// Set the rest of the flags for a static route.
|
||||||
data->ValidLifetime = 0xffffffff;
|
data->ValidLifetime = 0xffffffff;
|
||||||
data->PreferredLifetime = 0xffffffff;
|
data->PreferredLifetime = 0xffffffff;
|
||||||
data->Metric = 0;
|
data->Metric = EXCLUSION_ROUTE_METRIC;
|
||||||
data->Protocol = MIB_IPPROTO_NETMGMT;
|
data->Protocol = MIB_IPPROTO_NETMGMT;
|
||||||
data->Loopback = false;
|
data->Loopback = false;
|
||||||
data->AutoconfigureAddress = false;
|
data->AutoconfigureAddress = false;
|
||||||
@@ -254,7 +426,8 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
|||||||
delete data;
|
delete data;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
updateValidInterfaces(family);
|
updateInterfaceMetrics(family);
|
||||||
|
updateCapturedRoutes(family, table);
|
||||||
updateExclusionRoute(data, table);
|
updateExclusionRoute(data, table);
|
||||||
FreeMibTable(table);
|
FreeMibTable(table);
|
||||||
|
|
||||||
@@ -266,26 +439,28 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
|||||||
logger.debug() << "Deleting exclusion route for"
|
logger.debug() << "Deleting exclusion route for"
|
||||||
<< logger.sensitive(prefix.address().toString());
|
<< logger.sensitive(prefix.address().toString());
|
||||||
|
|
||||||
for (;;) {
|
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
||||||
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
if (data == nullptr) {
|
||||||
if (data == nullptr) {
|
return true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
|
||||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
|
||||||
logger.error() << "Failed to delete route to"
|
|
||||||
<< logger.sensitive(prefix.toString())
|
|
||||||
<< "result:" << result;
|
|
||||||
}
|
|
||||||
delete data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
|
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||||
|
logger.error() << "Failed to delete route to"
|
||||||
|
<< logger.sensitive(prefix.toString())
|
||||||
|
<< "result:" << result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captured routes might have changed.
|
||||||
|
updateCapturedRoutes(data->DestinationPrefix.Prefix.si_family);
|
||||||
|
|
||||||
|
delete data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::flushExclusionRoutes() {
|
void WindowsRouteMonitor::flushRouteTable(
|
||||||
for (auto i = m_exclusionRoutes.begin(); i != m_exclusionRoutes.end(); i++) {
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*>& table) {
|
||||||
|
for (auto i = table.begin(); i != table.end(); i++) {
|
||||||
MIB_IPFORWARD_ROW2* data = i.value();
|
MIB_IPFORWARD_ROW2* data = i.value();
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||||
@@ -295,7 +470,17 @@ void WindowsRouteMonitor::flushExclusionRoutes() {
|
|||||||
}
|
}
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
m_exclusionRoutes.clear();
|
table.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::setDetaultRouteCapture(bool enable) {
|
||||||
|
m_defaultRouteCapture = enable;
|
||||||
|
|
||||||
|
// Flush any captured routes when disabling the feature.
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
flushRouteTable(m_clonedRoutes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::routeChanged() {
|
void WindowsRouteMonitor::routeChanged() {
|
||||||
@@ -308,7 +493,8 @@ void WindowsRouteMonitor::routeChanged() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValidInterfaces(AF_UNSPEC);
|
updateInterfaceMetrics(AF_UNSPEC);
|
||||||
|
updateCapturedRoutes(AF_UNSPEC, table);
|
||||||
for (MIB_IPFORWARD_ROW2* data : m_exclusionRoutes) {
|
for (MIB_IPFORWARD_ROW2* data : m_exclusionRoutes) {
|
||||||
updateExclusionRoute(data, table);
|
updateExclusionRoute(data, table);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2ipdef.h>
|
#include <ws2ipdef.h>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "ipaddress.h"
|
#include "ipaddress.h"
|
||||||
@@ -19,28 +21,41 @@ class WindowsRouteMonitor final : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindowsRouteMonitor(QObject* parent);
|
WindowsRouteMonitor(quint64 luid, QObject* parent);
|
||||||
~WindowsRouteMonitor();
|
~WindowsRouteMonitor();
|
||||||
|
|
||||||
|
void setDetaultRouteCapture(bool enable);
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix);
|
bool addExclusionRoute(const IPAddress& prefix);
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix);
|
bool deleteExclusionRoute(const IPAddress& prefix);
|
||||||
void flushExclusionRoutes();
|
void flushExclusionRoutes() { return flushRouteTable(m_exclusionRoutes); };
|
||||||
|
|
||||||
void setLuid(quint64 luid) { m_luid = luid; }
|
quint64 getLuid() const { return m_luid; }
|
||||||
quint64 getLuid() { return m_luid; }
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void routeChanged();
|
void routeChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const;
|
||||||
|
static bool routeContainsDest(const IP_ADDRESS_PREFIX* route,
|
||||||
|
const IP_ADDRESS_PREFIX* dest);
|
||||||
|
static QHostAddress prefixToAddress(const IP_ADDRESS_PREFIX* dest);
|
||||||
|
|
||||||
|
void flushRouteTable(QHash<IPAddress, MIB_IPFORWARD_ROW2*>& table);
|
||||||
void updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void* table);
|
void updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void* table);
|
||||||
void updateValidInterfaces(int family);
|
void updateInterfaceMetrics(int family);
|
||||||
|
void updateCapturedRoutes(int family);
|
||||||
|
void updateCapturedRoutes(int family, void* table);
|
||||||
|
|
||||||
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_exclusionRoutes;
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_exclusionRoutes;
|
||||||
QList<quint64> m_validInterfacesIpv4;
|
QMap<quint64, ULONG> m_interfaceMetricsIpv4;
|
||||||
QList<quint64> m_validInterfacesIpv6;
|
QMap<quint64, ULONG> m_interfaceMetricsIpv6;
|
||||||
|
|
||||||
quint64 m_luid = 0;
|
// Default route cloning
|
||||||
|
bool m_defaultRouteCapture = false;
|
||||||
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_clonedRoutes;
|
||||||
|
|
||||||
|
const quint64 m_luid = 0;
|
||||||
HANDLE m_routeHandle = INVALID_HANDLE_VALUE;
|
HANDLE m_routeHandle = INVALID_HANDLE_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,15 @@
|
|||||||
|
|
||||||
#include "windowssplittunnel.h"
|
#include "windowssplittunnel.h"
|
||||||
|
|
||||||
|
#include <qassert.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "../windowscommons.h"
|
#include "../windowscommons.h"
|
||||||
#include "../windowsservicemanager.h"
|
#include "../windowsservicemanager.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "platforms/windows/daemon/windowsfirewall.h"
|
||||||
|
#include "platforms/windows/daemon/windowssplittunnel.h"
|
||||||
#include "platforms/windows/windowsutils.h"
|
#include "platforms/windows/windowsutils.h"
|
||||||
#include "windowsfirewall.h"
|
#include "windowsfirewall.h"
|
||||||
|
|
||||||
@@ -18,34 +24,252 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
#include <QThread>
|
|
||||||
|
#pragma region
|
||||||
|
|
||||||
|
// Driver Configuration structures
|
||||||
|
using CONFIGURATION_ENTRY = struct {
|
||||||
|
// Offset into buffer region that follows all entries.
|
||||||
|
// The image name uses the device path.
|
||||||
|
SIZE_T ImageNameOffset;
|
||||||
|
// Length of the String
|
||||||
|
USHORT ImageNameLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using CONFIGURATION_HEADER = struct {
|
||||||
|
// Number of entries immediately following the header.
|
||||||
|
SIZE_T NumEntries;
|
||||||
|
|
||||||
|
// Total byte length: header + entries + string buffer.
|
||||||
|
SIZE_T TotalLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to Configure Which IP is network/vpn
|
||||||
|
using IP_ADDRESSES_CONFIG = struct {
|
||||||
|
IN_ADDR TunnelIpv4;
|
||||||
|
IN_ADDR InternetIpv4;
|
||||||
|
|
||||||
|
IN6_ADDR TunnelIpv6;
|
||||||
|
IN6_ADDR InternetIpv6;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to Define Which Processes are alive on activation
|
||||||
|
using PROCESS_DISCOVERY_HEADER = struct {
|
||||||
|
SIZE_T NumEntries;
|
||||||
|
SIZE_T TotalLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PROCESS_DISCOVERY_ENTRY = struct {
|
||||||
|
HANDLE ProcessId;
|
||||||
|
HANDLE ParentProcessId;
|
||||||
|
|
||||||
|
SIZE_T ImageNameOffset;
|
||||||
|
USHORT ImageNameLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ProcessInfo = struct {
|
||||||
|
DWORD ProcessId;
|
||||||
|
DWORD ParentProcessId;
|
||||||
|
FILETIME CreationTime;
|
||||||
|
std::wstring DevicePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef CTL_CODE
|
||||||
|
|
||||||
|
# define FILE_ANY_ACCESS 0x0000
|
||||||
|
|
||||||
|
# define METHOD_BUFFERED 0
|
||||||
|
# define METHOD_IN_DIRECT 1
|
||||||
|
# define METHOD_NEITHER 3
|
||||||
|
|
||||||
|
# define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||||
|
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Known ControlCodes
|
||||||
|
#define IOCTL_INITIALIZE CTL_CODE(0x8000, 1, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_DEQUEUE_EVENT \
|
||||||
|
CTL_CODE(0x8000, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_REGISTER_PROCESSES \
|
||||||
|
CTL_CODE(0x8000, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_REGISTER_IP_ADDRESSES \
|
||||||
|
CTL_CODE(0x8000, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_IP_ADDRESSES \
|
||||||
|
CTL_CODE(0x8000, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_SET_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_CLEAR_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 8, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_STATE CTL_CODE(0x8000, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_QUERY_PROCESS \
|
||||||
|
CTL_CODE(0x8000, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_ST_RESET CTL_CODE(0x8000, 11, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
constexpr static const auto DRIVER_SYMLINK = L"\\\\.\\MULLVADSPLITTUNNEL";
|
||||||
|
constexpr static const auto DRIVER_FILENAME = "mullvad-split-tunnel.sys";
|
||||||
|
constexpr static const auto DRIVER_SERVICE_NAME = L"AmneziaVPNSplitTunnel";
|
||||||
|
constexpr static const auto MV_SERVICE_NAME = L"MullvadVPN";
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("WindowsSplitTunnel");
|
Logger logger("WindowsSplitTunnel");
|
||||||
|
|
||||||
|
ProcessInfo getProcessInfo(HANDLE process, const PROCESSENTRY32W& processMeta) {
|
||||||
|
ProcessInfo pi;
|
||||||
|
pi.ParentProcessId = processMeta.th32ParentProcessID;
|
||||||
|
pi.ProcessId = processMeta.th32ProcessID;
|
||||||
|
pi.CreationTime = {0, 0};
|
||||||
|
pi.DevicePath = L"";
|
||||||
|
|
||||||
|
FILETIME creationTime, null_time;
|
||||||
|
auto ok = GetProcessTimes(process, &creationTime, &null_time, &null_time,
|
||||||
|
&null_time);
|
||||||
|
if (ok) {
|
||||||
|
pi.CreationTime = creationTime;
|
||||||
|
}
|
||||||
|
wchar_t imagepath[MAX_PATH + 1];
|
||||||
|
if (K32GetProcessImageFileNameW(
|
||||||
|
process, imagepath, sizeof(imagepath) / sizeof(*imagepath)) != 0) {
|
||||||
|
pi.DevicePath = imagepath;
|
||||||
|
}
|
||||||
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsSplitTunnel::WindowsSplitTunnel(QObject* parent) : QObject(parent) {
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<WindowsSplitTunnel> WindowsSplitTunnel::create(
|
||||||
|
WindowsFirewall* fw) {
|
||||||
|
if (fw == nullptr) {
|
||||||
|
// Pre-Condition:
|
||||||
|
// Make sure the Windows Firewall has created the sublayer
|
||||||
|
// otherwise the driver will fail to initialize
|
||||||
|
logger.error() << "Failed to did not pass a WindowsFirewall obj"
|
||||||
|
<< "The Driver cannot work with the sublayer not created";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// 00: Check if we conflict with mullvad, if so.
|
||||||
if (detectConflict()) {
|
if (detectConflict()) {
|
||||||
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
||||||
uninstallDriver();
|
return nullptr;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// 01: Check if the driver is installed, if not do so.
|
||||||
m_tries = 0;
|
|
||||||
|
|
||||||
if (!isInstalled()) {
|
if (!isInstalled()) {
|
||||||
logger.debug() << "Driver is not Installed, doing so";
|
logger.debug() << "Driver is not Installed, doing so";
|
||||||
auto handle = installDriver();
|
auto handle = installDriver();
|
||||||
if (handle == INVALID_HANDLE_VALUE) {
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
WindowsUtils::windowsLog("Failed to install Driver");
|
WindowsUtils::windowsLog("Failed to install Driver");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
logger.debug() << "Driver installed";
|
logger.debug() << "Driver installed";
|
||||||
CloseServiceHandle(handle);
|
CloseServiceHandle(handle);
|
||||||
} else {
|
} else {
|
||||||
logger.debug() << "Driver is installed";
|
logger.debug() << "Driver was installed";
|
||||||
}
|
}
|
||||||
initDriver();
|
// 02: Now check if the service is running
|
||||||
|
auto driver_manager =
|
||||||
|
WindowsServiceManager::open(QString::fromWCharArray(DRIVER_SERVICE_NAME));
|
||||||
|
if (Q_UNLIKELY(driver_manager == nullptr)) {
|
||||||
|
// Let's be fair if we end up here,
|
||||||
|
// after checking it exists and installing it,
|
||||||
|
// this is super unlikeley
|
||||||
|
Q_ASSERT(false);
|
||||||
|
logger.error()
|
||||||
|
<< "WindowsServiceManager was unable fo find Split Tunnel service?";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!driver_manager->isRunning()) {
|
||||||
|
logger.debug() << "Driver is not running, starting it";
|
||||||
|
// Start the service
|
||||||
|
if (!driver_manager->startService()) {
|
||||||
|
logger.error() << "Failed to start Split Tunnel Service";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 03: Open the Driver Symlink
|
||||||
|
auto driverFile = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
||||||
|
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
;
|
||||||
|
if (driverFile == INVALID_HANDLE_VALUE) {
|
||||||
|
WindowsUtils::windowsLog("Failed to open Driver: ");
|
||||||
|
// Only once, if the opening did not work. Try to reboot it. #
|
||||||
|
logger.info()
|
||||||
|
<< "Failed to open driver, attempting only once to reboot driver";
|
||||||
|
if (!driver_manager->stopService()) {
|
||||||
|
logger.error() << "Unable stop driver";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
logger.info() << "Stopped driver, starting it again.";
|
||||||
|
if (!driver_manager->startService()) {
|
||||||
|
logger.error() << "Unable start driver";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
logger.info() << "Opening again.";
|
||||||
|
driverFile = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
||||||
|
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
if (driverFile == INVALID_HANDLE_VALUE) {
|
||||||
|
logger.error() << "Opening Failed again, sorry!";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!initDriver(driverFile)) {
|
||||||
|
logger.error() << "Failed to init driver";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// We're ready to talk to the driver, it's alive and setup.
|
||||||
|
return std::make_unique<WindowsSplitTunnel>(driverFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsSplitTunnel::initDriver(HANDLE driverIO) {
|
||||||
|
// We need to now check the state and init it, if required
|
||||||
|
auto state = getState(driverIO);
|
||||||
|
if (state == STATE_UNKNOWN) {
|
||||||
|
logger.debug() << "Cannot check if driver is initialized";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state >= STATE_INITIALIZED) {
|
||||||
|
logger.debug() << "Driver already initialized: " << state;
|
||||||
|
// Reset Driver as it has wfp handles probably >:(
|
||||||
|
resetDriver(driverIO);
|
||||||
|
|
||||||
|
auto newState = getState(driverIO);
|
||||||
|
logger.debug() << "New state after reset:" << newState;
|
||||||
|
if (newState >= STATE_INITIALIZED) {
|
||||||
|
logger.debug() << "Reset unsuccesfull";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytesReturned;
|
||||||
|
auto ok = DeviceIoControl(driverIO, IOCTL_INITIALIZE, nullptr, 0, nullptr, 0,
|
||||||
|
&bytesReturned, nullptr);
|
||||||
|
if (!ok) {
|
||||||
|
auto err = GetLastError();
|
||||||
|
logger.error() << "Driver init failed err -" << err;
|
||||||
|
logger.error() << "State:" << getState(driverIO);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.debug() << "Driver initialized" << getState(driverIO);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsSplitTunnel::WindowsSplitTunnel(HANDLE driverIO) : m_driver(driverIO) {
|
||||||
|
logger.debug() << "Connected to the Driver";
|
||||||
|
|
||||||
|
Q_ASSERT(getState() == STATE_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsSplitTunnel::~WindowsSplitTunnel() {
|
WindowsSplitTunnel::~WindowsSplitTunnel() {
|
||||||
@@ -53,73 +277,12 @@ WindowsSplitTunnel::~WindowsSplitTunnel() {
|
|||||||
uninstallDriver();
|
uninstallDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::initDriver() {
|
bool WindowsSplitTunnel::excludeApps(const QStringList& appPaths) {
|
||||||
if (detectConflict()) {
|
|
||||||
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug() << "Try to open Split Tunnel Driver";
|
|
||||||
// Open the Driver Symlink
|
|
||||||
m_driver = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
|
||||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
||||||
;
|
|
||||||
if (m_driver == INVALID_HANDLE_VALUE && m_tries < 500) {
|
|
||||||
WindowsUtils::windowsLog("Failed to open Driver: ");
|
|
||||||
m_tries++;
|
|
||||||
Sleep(100);
|
|
||||||
// If the handle is not present, try again after the serivce has started;
|
|
||||||
auto driver_manager = WindowsServiceManager(DRIVER_SERVICE_NAME);
|
|
||||||
QObject::connect(&driver_manager, &WindowsServiceManager::serviceStarted,
|
|
||||||
this, &WindowsSplitTunnel::initDriver);
|
|
||||||
driver_manager.startService();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug() << "Connected to the Driver";
|
|
||||||
// Reset Driver as it has wfp handles probably >:(
|
|
||||||
|
|
||||||
if (!WindowsFirewall::instance()->init()) {
|
|
||||||
logger.error() << "Init WFP-Sublayer failed, driver won't be functional";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to now check the state and init it, if required
|
|
||||||
|
|
||||||
auto state = getState();
|
|
||||||
if (state == STATE_UNKNOWN) {
|
|
||||||
logger.debug() << "Cannot check if driver is initialized";
|
|
||||||
}
|
|
||||||
if (state >= STATE_INITIALIZED) {
|
|
||||||
logger.debug() << "Driver already initialized: " << state;
|
|
||||||
reset();
|
|
||||||
|
|
||||||
auto newState = getState();
|
|
||||||
logger.debug() << "New state after reset:" << newState;
|
|
||||||
if (newState >= STATE_INITIALIZED) {
|
|
||||||
logger.debug() << "Reset unsuccesfull";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD bytesReturned;
|
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_INITIALIZE, nullptr, 0, nullptr, 0,
|
|
||||||
&bytesReturned, nullptr);
|
|
||||||
if (!ok) {
|
|
||||||
auto err = GetLastError();
|
|
||||||
logger.error() << "Driver init failed err -" << err;
|
|
||||||
logger.error() << "State:" << getState();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug() << "Driver initialized" << getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowsSplitTunnel::setRules(const QStringList& appPaths) {
|
|
||||||
auto state = getState();
|
auto state = getState();
|
||||||
if (state != STATE_READY && state != STATE_RUNNING) {
|
if (state != STATE_READY && state != STATE_RUNNING) {
|
||||||
logger.warning() << "Driver is not in the right State to set Rules"
|
logger.warning() << "Driver is not in the right State to set Rules"
|
||||||
<< state;
|
<< state;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "Pushing new Ruleset for Split-Tunnel " << state;
|
logger.debug() << "Pushing new Ruleset for Split-Tunnel " << state;
|
||||||
@@ -133,12 +296,13 @@ void WindowsSplitTunnel::setRules(const QStringList& appPaths) {
|
|||||||
auto err = GetLastError();
|
auto err = GetLastError();
|
||||||
WindowsUtils::windowsLog("Set Config Failed:");
|
WindowsUtils::windowsLog("Set Config Failed:");
|
||||||
logger.error() << "Failed to set Config err code " << err;
|
logger.error() << "Failed to set Config err code " << err;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "New Configuration applied: " << getState();
|
logger.debug() << "New Configuration applied: " << stateString();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
bool WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
// To Start we need to send 2 things:
|
// To Start we need to send 2 things:
|
||||||
// Network info (what is vpn what is network)
|
// Network info (what is vpn what is network)
|
||||||
logger.debug() << "Starting SplitTunnel";
|
logger.debug() << "Starting SplitTunnel";
|
||||||
@@ -151,7 +315,7 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
|||||||
0, &bytesReturned, nullptr);
|
0, &bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Driver init failed";
|
logger.error() << "Driver init failed";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,16 +328,16 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
|||||||
nullptr);
|
nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Failed to set Process Config";
|
logger.error() << "Failed to set Process Config";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Set Process Config ok || new State:" << getState();
|
logger.debug() << "Set Process Config ok || new State:" << stateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getState() == STATE_INITIALIZED) {
|
if (getState() == STATE_INITIALIZED) {
|
||||||
logger.warning() << "Driver is still not ready after process list send";
|
logger.warning() << "Driver is still not ready after process list send";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Driver is ready || new State:" << getState();
|
logger.debug() << "Driver is ready || new State:" << stateString();
|
||||||
|
|
||||||
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
||||||
@@ -181,9 +345,10 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
|||||||
nullptr);
|
nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Failed to set Network Config";
|
logger.error() << "Failed to set Network Config";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "New Network Config Applied || new State:" << getState();
|
logger.debug() << "New Network Config Applied || new State:" << stateString();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::stop() {
|
void WindowsSplitTunnel::stop() {
|
||||||
@@ -197,25 +362,27 @@ void WindowsSplitTunnel::stop() {
|
|||||||
logger.debug() << "Stopping Split tunnel successfull";
|
logger.debug() << "Stopping Split tunnel successfull";
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::reset() {
|
bool WindowsSplitTunnel::resetDriver(HANDLE driverIO) {
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_ST_RESET, nullptr, 0, nullptr, 0,
|
auto ok = DeviceIoControl(driverIO, IOCTL_ST_RESET, nullptr, 0, nullptr, 0,
|
||||||
&bytesReturned, nullptr);
|
&bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Reset Split tunnel not successfull";
|
logger.error() << "Reset Split tunnel not successfull";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Reset Split tunnel successfull";
|
logger.debug() << "Reset Split tunnel successfull";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRIVER_STATE WindowsSplitTunnel::getState() {
|
// static
|
||||||
if (m_driver == INVALID_HANDLE_VALUE) {
|
WindowsSplitTunnel::DRIVER_STATE WindowsSplitTunnel::getState(HANDLE driverIO) {
|
||||||
|
if (driverIO == INVALID_HANDLE_VALUE) {
|
||||||
logger.debug() << "Can't query State from non Opened Driver";
|
logger.debug() << "Can't query State from non Opened Driver";
|
||||||
return STATE_UNKNOWN;
|
return STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
SIZE_T outBuffer;
|
SIZE_T outBuffer;
|
||||||
bool ok = DeviceIoControl(m_driver, IOCTL_GET_STATE, nullptr, 0, &outBuffer,
|
bool ok = DeviceIoControl(driverIO, IOCTL_GET_STATE, nullptr, 0, &outBuffer,
|
||||||
sizeof(outBuffer), &bytesReturned, nullptr);
|
sizeof(outBuffer), &bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
WindowsUtils::windowsLog("getState response failure");
|
WindowsUtils::windowsLog("getState response failure");
|
||||||
@@ -225,7 +392,10 @@ DRIVER_STATE WindowsSplitTunnel::getState() {
|
|||||||
WindowsUtils::windowsLog("getState response is empty");
|
WindowsUtils::windowsLog("getState response is empty");
|
||||||
return STATE_UNKNOWN;
|
return STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
return static_cast<DRIVER_STATE>(outBuffer);
|
return static_cast<WindowsSplitTunnel::DRIVER_STATE>(outBuffer);
|
||||||
|
}
|
||||||
|
WindowsSplitTunnel::DRIVER_STATE WindowsSplitTunnel::getState() {
|
||||||
|
return getState(m_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
||||||
@@ -273,58 +443,59 @@ std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
|||||||
return outBuffer;
|
return outBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateIPConfiguration(
|
std::vector<std::byte> WindowsSplitTunnel::generateIPConfiguration(
|
||||||
int inetAdapterIndex, int vpnAdapterIndex) {
|
int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
std::vector<uint8_t> out(sizeof(IP_ADDRESSES_CONFIG));
|
std::vector<std::byte> out(sizeof(IP_ADDRESSES_CONFIG));
|
||||||
|
|
||||||
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
||||||
|
|
||||||
auto ifaces = QNetworkInterface::allInterfaces();
|
auto ifaces = QNetworkInterface::allInterfaces();
|
||||||
|
|
||||||
if (vpnAdapterIndex == 0) {
|
if (vpnAdapterIndex == 0) {
|
||||||
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always the VPN
|
// Always the VPN
|
||||||
getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
if (!getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
||||||
&config->TunnelIpv6);
|
&config->TunnelIpv6)) {
|
||||||
// 2nd best route
|
return {};
|
||||||
getAddress(inetAdapterIndex, &config->InternetIpv4, &config->InternetIpv6);
|
}
|
||||||
|
// 2nd best route is usually the internet adapter
|
||||||
|
if (!getAddress(inetAdapterIndex, &config->InternetIpv4,
|
||||||
|
&config->InternetIpv6)) {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
void WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
bool WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||||
IN6_ADDR* out_ipv6) {
|
IN6_ADDR* out_ipv6) {
|
||||||
QNetworkInterface target =
|
QNetworkInterface target =
|
||||||
QNetworkInterface::interfaceFromIndex(adapterIndex);
|
QNetworkInterface::interfaceFromIndex(adapterIndex);
|
||||||
logger.debug() << "Getting adapter info for:" << target.humanReadableName();
|
logger.debug() << "Getting adapter info for:" << target.humanReadableName();
|
||||||
|
|
||||||
// take the first v4/v6 Adress and convert to in_addr
|
auto get = [&target](QAbstractSocket::NetworkLayerProtocol protocol) {
|
||||||
for (auto address : target.addressEntries()) {
|
for (auto address : target.addressEntries()) {
|
||||||
if (address.ip().protocol() == QAbstractSocket::IPv4Protocol) {
|
if (address.ip().protocol() != protocol) {
|
||||||
auto adrr = address.ip().toString();
|
continue;
|
||||||
std::wstring wstr = adrr.toStdWString();
|
|
||||||
logger.debug() << "IpV4" << logger.sensitive(adrr);
|
|
||||||
PCWSTR w_str_ip = wstr.c_str();
|
|
||||||
auto ok = InetPtonW(AF_INET, w_str_ip, out_ipv4);
|
|
||||||
if (ok != 1) {
|
|
||||||
logger.debug() << "Ipv4 Conversation error" << WSAGetLastError();
|
|
||||||
}
|
}
|
||||||
break;
|
return address.ip().toString().toStdWString();
|
||||||
}
|
}
|
||||||
|
return std::wstring{};
|
||||||
|
};
|
||||||
|
auto ipv4 = get(QAbstractSocket::IPv4Protocol);
|
||||||
|
auto ipv6 = get(QAbstractSocket::IPv6Protocol);
|
||||||
|
|
||||||
|
if (InetPtonW(AF_INET, ipv4.c_str(), out_ipv4) != 1) {
|
||||||
|
logger.debug() << "Ipv4 Conversation error" << WSAGetLastError();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
for (auto address : target.addressEntries()) {
|
if (ipv6.empty()) {
|
||||||
if (address.ip().protocol() == QAbstractSocket::IPv6Protocol) {
|
std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
||||||
auto adrr = address.ip().toString();
|
return true;
|
||||||
std::wstring wstr = adrr.toStdWString();
|
|
||||||
logger.debug() << "IpV6" << logger.sensitive(adrr);
|
|
||||||
PCWSTR w_str_ip = wstr.c_str();
|
|
||||||
auto ok = InetPtonW(AF_INET6, w_str_ip, out_ipv6);
|
|
||||||
if (ok != 1) {
|
|
||||||
logger.error() << "Ipv6 Conversation error" << WSAGetLastError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (InetPtonW(AF_INET6, ipv6.c_str(), out_ipv6) != 1) {
|
||||||
|
logger.debug() << "Ipv6 Conversation error" << WSAGetLastError();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
||||||
@@ -411,33 +582,6 @@ std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::close() {
|
|
||||||
CloseHandle(m_driver);
|
|
||||||
m_driver = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessInfo WindowsSplitTunnel::getProcessInfo(
|
|
||||||
HANDLE process, const PROCESSENTRY32W& processMeta) {
|
|
||||||
ProcessInfo pi;
|
|
||||||
pi.ParentProcessId = processMeta.th32ParentProcessID;
|
|
||||||
pi.ProcessId = processMeta.th32ProcessID;
|
|
||||||
pi.CreationTime = {0, 0};
|
|
||||||
pi.DevicePath = L"";
|
|
||||||
|
|
||||||
FILETIME creationTime, null_time;
|
|
||||||
auto ok = GetProcessTimes(process, &creationTime, &null_time, &null_time,
|
|
||||||
&null_time);
|
|
||||||
if (ok) {
|
|
||||||
pi.CreationTime = creationTime;
|
|
||||||
}
|
|
||||||
wchar_t imagepath[MAX_PATH + 1];
|
|
||||||
if (K32GetProcessImageFileNameW(
|
|
||||||
process, imagepath, sizeof(imagepath) / sizeof(*imagepath)) != 0) {
|
|
||||||
pi.DevicePath = imagepath;
|
|
||||||
}
|
|
||||||
return pi;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
SC_HANDLE WindowsSplitTunnel::installDriver() {
|
SC_HANDLE WindowsSplitTunnel::installDriver() {
|
||||||
LPCWSTR displayName = L"Amnezia Split Tunnel Service";
|
LPCWSTR displayName = L"Amnezia Split Tunnel Service";
|
||||||
@@ -448,15 +592,15 @@ SC_HANDLE WindowsSplitTunnel::installDriver() {
|
|||||||
return (SC_HANDLE)INVALID_HANDLE_VALUE;
|
return (SC_HANDLE)INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
auto path = driver.absolutePath() + "/" + DRIVER_FILENAME;
|
auto path = driver.absolutePath() + "/" + DRIVER_FILENAME;
|
||||||
LPCWSTR binPath = (const wchar_t*)path.utf16();
|
auto binPath = (const wchar_t*)path.utf16();
|
||||||
auto scm_rights = SC_MANAGER_ALL_ACCESS;
|
auto scm_rights = SC_MANAGER_ALL_ACCESS;
|
||||||
auto serviceManager = OpenSCManager(NULL, // local computer
|
auto serviceManager = OpenSCManager(nullptr, // local computer
|
||||||
NULL, // servicesActive database
|
nullptr, // servicesActive database
|
||||||
scm_rights);
|
scm_rights);
|
||||||
auto service = CreateService(serviceManager, DRIVER_SERVICE_NAME, displayName,
|
auto service = CreateService(
|
||||||
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
|
serviceManager, DRIVER_SERVICE_NAME, displayName, SERVICE_ALL_ACCESS,
|
||||||
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
|
SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, binPath,
|
||||||
binPath, nullptr, 0, nullptr, nullptr, nullptr);
|
nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||||
CloseServiceHandle(serviceManager);
|
CloseServiceHandle(serviceManager);
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@@ -554,3 +698,25 @@ bool WindowsSplitTunnel::detectConflict() {
|
|||||||
CloseServiceHandle(servicehandle);
|
CloseServiceHandle(servicehandle);
|
||||||
return err == ERROR_SERVICE_DOES_NOT_EXIST;
|
return err == ERROR_SERVICE_DOES_NOT_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsSplitTunnel::isRunning() { return getState() == STATE_RUNNING; }
|
||||||
|
QString WindowsSplitTunnel::stateString() {
|
||||||
|
switch (getState()) {
|
||||||
|
case STATE_UNKNOWN:
|
||||||
|
return "STATE_UNKNOWN";
|
||||||
|
case STATE_NONE:
|
||||||
|
return "STATE_NONE";
|
||||||
|
case STATE_STARTED:
|
||||||
|
return "STATE_STARTED";
|
||||||
|
case STATE_INITIALIZED:
|
||||||
|
return "STATE_INITIALIZED";
|
||||||
|
case STATE_READY:
|
||||||
|
return "STATE_READY";
|
||||||
|
case STATE_RUNNING:
|
||||||
|
return "STATE_RUNNING";
|
||||||
|
case STATE_ZOMBIE:
|
||||||
|
return "STATE_ZOMBIE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
// Note: the ws2tcpip.h import must come before the others.
|
// Note: the ws2tcpip.h import must come before the others.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -18,160 +19,78 @@
|
|||||||
#include <tlhelp32.h>
|
#include <tlhelp32.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
// States for GetState
|
class WindowsFirewall;
|
||||||
enum DRIVER_STATE {
|
|
||||||
STATE_UNKNOWN = -1,
|
|
||||||
STATE_NONE = 0,
|
|
||||||
STATE_STARTED = 1,
|
|
||||||
STATE_INITIALIZED = 2,
|
|
||||||
STATE_READY = 3,
|
|
||||||
STATE_RUNNING = 4,
|
|
||||||
STATE_ZOMBIE = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef CTL_CODE
|
class WindowsSplitTunnel final {
|
||||||
|
|
||||||
# define FILE_ANY_ACCESS 0x0000
|
|
||||||
|
|
||||||
# define METHOD_BUFFERED 0
|
|
||||||
# define METHOD_IN_DIRECT 1
|
|
||||||
# define METHOD_NEITHER 3
|
|
||||||
|
|
||||||
# define CTL_CODE(DeviceType, Function, Method, Access) \
|
|
||||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Known ControlCodes
|
|
||||||
#define IOCTL_INITIALIZE CTL_CODE(0x8000, 1, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_DEQUEUE_EVENT \
|
|
||||||
CTL_CODE(0x8000, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_REGISTER_PROCESSES \
|
|
||||||
CTL_CODE(0x8000, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_REGISTER_IP_ADDRESSES \
|
|
||||||
CTL_CODE(0x8000, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_IP_ADDRESSES \
|
|
||||||
CTL_CODE(0x8000, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_SET_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_CLEAR_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 8, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_STATE CTL_CODE(0x8000, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_QUERY_PROCESS \
|
|
||||||
CTL_CODE(0x8000, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_ST_RESET CTL_CODE(0x8000, 11, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
// Driver Configuration structures
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Offset into buffer region that follows all entries.
|
|
||||||
// The image name uses the device path.
|
|
||||||
SIZE_T ImageNameOffset;
|
|
||||||
// Length of the String
|
|
||||||
USHORT ImageNameLength;
|
|
||||||
} CONFIGURATION_ENTRY;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Number of entries immediately following the header.
|
|
||||||
SIZE_T NumEntries;
|
|
||||||
|
|
||||||
// Total byte length: header + entries + string buffer.
|
|
||||||
SIZE_T TotalLength;
|
|
||||||
} CONFIGURATION_HEADER;
|
|
||||||
|
|
||||||
// Used to Configure Which IP is network/vpn
|
|
||||||
typedef struct {
|
|
||||||
IN_ADDR TunnelIpv4;
|
|
||||||
IN_ADDR InternetIpv4;
|
|
||||||
|
|
||||||
IN6_ADDR TunnelIpv6;
|
|
||||||
IN6_ADDR InternetIpv6;
|
|
||||||
} IP_ADDRESSES_CONFIG;
|
|
||||||
|
|
||||||
// Used to Define Which Processes are alive on activation
|
|
||||||
typedef struct {
|
|
||||||
SIZE_T NumEntries;
|
|
||||||
SIZE_T TotalLength;
|
|
||||||
} PROCESS_DISCOVERY_HEADER;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
HANDLE ProcessId;
|
|
||||||
HANDLE ParentProcessId;
|
|
||||||
|
|
||||||
SIZE_T ImageNameOffset;
|
|
||||||
USHORT ImageNameLength;
|
|
||||||
} PROCESS_DISCOVERY_ENTRY;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DWORD ProcessId;
|
|
||||||
DWORD ParentProcessId;
|
|
||||||
FILETIME CreationTime;
|
|
||||||
std::wstring DevicePath;
|
|
||||||
} ProcessInfo;
|
|
||||||
|
|
||||||
class WindowsSplitTunnel final : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY_MOVE(WindowsSplitTunnel)
|
|
||||||
public:
|
public:
|
||||||
explicit WindowsSplitTunnel(QObject* parent);
|
/**
|
||||||
|
* @brief Installs and Initializes the Split Tunnel Driver.
|
||||||
|
*
|
||||||
|
* @param fw -
|
||||||
|
* @return std::unique_ptr<WindowsSplitTunnel> - Is null on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<WindowsSplitTunnel> create(WindowsFirewall* fw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Windows Split Tunnel object
|
||||||
|
*
|
||||||
|
* @param driverIO - The Handle to the Driver's IO file, it assumes the driver
|
||||||
|
* is in STATE_INITIALIZED and the Firewall has been setup.
|
||||||
|
* Prefer using create() to get to this state.
|
||||||
|
*/
|
||||||
|
WindowsSplitTunnel(HANDLE driverIO);
|
||||||
|
/**
|
||||||
|
* @brief Destroy the Windows Split Tunnel object and uninstalls the Driver.
|
||||||
|
*/
|
||||||
~WindowsSplitTunnel();
|
~WindowsSplitTunnel();
|
||||||
|
|
||||||
// void excludeApps(const QStringList& paths);
|
// void excludeApps(const QStringList& paths);
|
||||||
// Excludes an Application from the VPN
|
// Excludes an Application from the VPN
|
||||||
void setRules(const QStringList& appPaths);
|
bool excludeApps(const QStringList& appPaths);
|
||||||
|
|
||||||
// Fetches and Pushed needed info to move to engaged mode
|
// Fetches and Pushed needed info to move to engaged mode
|
||||||
void start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
bool start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||||
// Deletes Rules and puts the driver into passive mode
|
// Deletes Rules and puts the driver into passive mode
|
||||||
void stop();
|
void stop();
|
||||||
// Resets the Whole Driver
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
// Just close connection, leave state as is
|
// Returns true if the split-tunnel driver is now up and running.
|
||||||
void close();
|
bool isRunning();
|
||||||
|
|
||||||
|
static bool detectConflict();
|
||||||
|
|
||||||
|
// States for GetState
|
||||||
|
enum DRIVER_STATE {
|
||||||
|
STATE_UNKNOWN = -1,
|
||||||
|
STATE_NONE = 0,
|
||||||
|
STATE_STARTED = 1,
|
||||||
|
STATE_INITIALIZED = 2,
|
||||||
|
STATE_READY = 3,
|
||||||
|
STATE_RUNNING = 4,
|
||||||
|
STATE_ZOMBIE = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
// Installes the Kernel Driver as Driver Service
|
// Installes the Kernel Driver as Driver Service
|
||||||
static SC_HANDLE installDriver();
|
static SC_HANDLE installDriver();
|
||||||
static bool uninstallDriver();
|
static bool uninstallDriver();
|
||||||
static bool isInstalled();
|
static bool isInstalled();
|
||||||
static bool detectConflict();
|
static bool initDriver(HANDLE driverIO);
|
||||||
|
static DRIVER_STATE getState(HANDLE driverIO);
|
||||||
|
static bool resetDriver(HANDLE driverIO);
|
||||||
|
|
||||||
private slots:
|
|
||||||
void initDriver();
|
|
||||||
|
|
||||||
private:
|
|
||||||
HANDLE m_driver = INVALID_HANDLE_VALUE;
|
HANDLE m_driver = INVALID_HANDLE_VALUE;
|
||||||
constexpr static const auto DRIVER_SYMLINK = L"\\\\.\\MULLVADSPLITTUNNEL";
|
|
||||||
constexpr static const auto DRIVER_FILENAME = "mullvad-split-tunnel.sys";
|
|
||||||
constexpr static const auto DRIVER_SERVICE_NAME = L"AmneziaVPNSplitTunnel";
|
|
||||||
constexpr static const auto MV_SERVICE_NAME = L"MullvadVPN";
|
|
||||||
DRIVER_STATE getState();
|
DRIVER_STATE getState();
|
||||||
|
QString stateString();
|
||||||
int m_tries;
|
|
||||||
// Initializes the WFP Sublayer
|
|
||||||
bool initSublayer();
|
|
||||||
|
|
||||||
// Generates a Configuration for Each APP
|
// Generates a Configuration for Each APP
|
||||||
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
||||||
// Generates a Configuration which IP's are VPN and which network
|
// Generates a Configuration which IP's are VPN and which network
|
||||||
std::vector<uint8_t> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
std::vector<std::byte> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||||
std::vector<uint8_t> generateProcessBlob();
|
std::vector<uint8_t> generateProcessBlob();
|
||||||
|
|
||||||
void getAddress(int adapterIndex, IN_ADDR* out_ipv4, IN6_ADDR* out_ipv6);
|
[[nodiscard]] bool getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||||
|
IN6_ADDR* out_ipv6);
|
||||||
// Collects info about an Opened Process
|
// Collects info about an Opened Process
|
||||||
ProcessInfo getProcessInfo(HANDLE process,
|
|
||||||
const PROCESSENTRY32W& processMeta);
|
|
||||||
|
|
||||||
// Converts a path to a Dos Path:
|
// Converts a path to a Dos Path:
|
||||||
// e.g C:/a.exe -> /harddisk0/a.exe
|
// e.g C:/a.exe -> /harddisk0/a.exe
|
||||||
|
|||||||
@@ -24,8 +24,20 @@ namespace {
|
|||||||
Logger logger("WireguardUtilsWindows");
|
Logger logger("WireguardUtilsWindows");
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
WireguardUtilsWindows::WireguardUtilsWindows(QObject* parent)
|
std::unique_ptr<WireguardUtilsWindows> WireguardUtilsWindows::create(
|
||||||
: WireguardUtils(parent), m_tunnel(this), m_routeMonitor(this) {
|
WindowsFirewall* fw, QObject* parent) {
|
||||||
|
if (!fw) {
|
||||||
|
logger.error() << "WireguardUtilsWindows::create: no wfp handle";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use make_unique here as the Constructor is private :(
|
||||||
|
auto utils = new WireguardUtilsWindows(parent, fw);
|
||||||
|
return std::unique_ptr<WireguardUtilsWindows>(utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
WireguardUtilsWindows::WireguardUtilsWindows(QObject* parent, WindowsFirewall* fw)
|
||||||
|
: WireguardUtils(parent), m_tunnel(this), m_firewall(fw) {
|
||||||
MZ_COUNT_CTOR(WireguardUtilsWindows);
|
MZ_COUNT_CTOR(WireguardUtilsWindows);
|
||||||
logger.debug() << "WireguardUtilsWindows created.";
|
logger.debug() << "WireguardUtilsWindows created.";
|
||||||
|
|
||||||
@@ -114,13 +126,13 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_luid = luid.Value;
|
m_luid = luid.Value;
|
||||||
m_routeMonitor.setLuid(luid.Value);
|
m_routeMonitor = new WindowsRouteMonitor(luid.Value, this);
|
||||||
|
|
||||||
if (config.m_killSwitchEnabled) {
|
if (config.m_killSwitchEnabled) {
|
||||||
// Enable the windows firewall
|
// Enable the windows firewall
|
||||||
NET_IFINDEX ifindex;
|
NET_IFINDEX ifindex;
|
||||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||||
WindowsFirewall::instance()->enableKillSwitch(ifindex);
|
m_firewall->enableInterface(ifindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "Registration completed";
|
logger.debug() << "Registration completed";
|
||||||
@@ -128,7 +140,11 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteInterface() {
|
bool WireguardUtilsWindows::deleteInterface() {
|
||||||
WindowsFirewall::instance()->disableKillSwitch();
|
if (m_routeMonitor) {
|
||||||
|
m_routeMonitor->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_firewall->disableKillSwitch();
|
||||||
m_tunnel.stop();
|
m_tunnel.stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -141,7 +157,7 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
|||||||
|
|
||||||
if (config.m_killSwitchEnabled) {
|
if (config.m_killSwitchEnabled) {
|
||||||
// Enable the windows firewall for this peer.
|
// Enable the windows firewall for this peer.
|
||||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
m_firewall->enablePeerTraffic(config);
|
||||||
}
|
}
|
||||||
logger.debug() << "Configuring peer" << publicKey.toHex()
|
logger.debug() << "Configuring peer" << publicKey.toHex()
|
||||||
<< "via" << config.m_serverIpv4AddrIn;
|
<< "via" << config.m_serverIpv4AddrIn;
|
||||||
@@ -171,9 +187,9 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exclude the server address, except for multihop exit servers.
|
// Exclude the server address, except for multihop exit servers.
|
||||||
if (config.m_hopType != InterfaceConfig::MultiHopExit) {
|
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||||
m_routeMonitor.addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||||
m_routeMonitor.addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString reply = m_tunnel.uapiCommand(message);
|
QString reply = m_tunnel.uapiCommand(message);
|
||||||
@@ -186,13 +202,13 @@ bool WireguardUtilsWindows::deletePeer(const InterfaceConfig& config) {
|
|||||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||||
|
|
||||||
// Clear exclustion routes for this peer.
|
// Clear exclustion routes for this peer.
|
||||||
if (config.m_hopType != InterfaceConfig::MultiHopExit) {
|
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||||
m_routeMonitor.deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||||
m_routeMonitor.deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable the windows firewall for this peer.
|
// Disable the windows firewall for this peer.
|
||||||
WindowsFirewall::instance()->disablePeerTraffic(config.m_serverPublicKey);
|
m_firewall->disablePeerTraffic(config.m_serverPublicKey);
|
||||||
|
|
||||||
QString message;
|
QString message;
|
||||||
QTextStream out(&message);
|
QTextStream out(&message);
|
||||||
@@ -238,6 +254,13 @@ void WireguardUtilsWindows::buildMibForwardRow(const IPAddress& prefix,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
|
if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||||
|
// If we are setting up a default route, instruct the route monitor to
|
||||||
|
// capture traffic to all non-excluded destinations
|
||||||
|
m_routeMonitor->setDetaultRouteCapture(true);
|
||||||
|
}
|
||||||
|
// Build the route
|
||||||
|
|
||||||
MIB_IPFORWARD_ROW2 entry;
|
MIB_IPFORWARD_ROW2 entry;
|
||||||
buildMibForwardRow(prefix, &entry);
|
buildMibForwardRow(prefix, &entry);
|
||||||
|
|
||||||
@@ -255,6 +278,12 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
||||||
|
if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||||
|
// Deactivate the route capture feature.
|
||||||
|
m_routeMonitor->setDetaultRouteCapture(false);
|
||||||
|
}
|
||||||
|
// Build the route
|
||||||
|
|
||||||
MIB_IPFORWARD_ROW2 entry;
|
MIB_IPFORWARD_ROW2 entry;
|
||||||
buildMibForwardRow(prefix, &entry);
|
buildMibForwardRow(prefix, &entry);
|
||||||
|
|
||||||
@@ -272,9 +301,28 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::addExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::addExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_routeMonitor.addExclusionRoute(prefix);
|
return m_routeMonitor->addExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_routeMonitor.deleteExclusionRoute(prefix);
|
return m_routeMonitor->deleteExclusionRoute(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsWindows::excludeLocalNetworks(
|
||||||
|
const QList<IPAddress>& addresses) {
|
||||||
|
// If the interface isn't up then something went horribly wrong.
|
||||||
|
Q_ASSERT(m_routeMonitor);
|
||||||
|
// For each destination - attempt to exclude it from the VPN tunnel.
|
||||||
|
bool result = true;
|
||||||
|
for (const IPAddress& prefix : addresses) {
|
||||||
|
if (!m_routeMonitor->addExclusionRoute(prefix)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Permit LAN traffic through the firewall.
|
||||||
|
if (!m_firewall->enableLanBypass(addresses)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||