Compare commits
208 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29d98846d3 | |||
| f6e7d3ccf1 | |||
| a754a11913 | |||
| 4d25e3b6f6 | |||
| 1fac280497 | |||
| c886c5e6a7 | |||
| cd7f78b9ca | |||
| a587d3230f | |||
| 93e7b45136 | |||
| e024f71ce1 | |||
| 50d1be7b4a | |||
| 3ec6d8973b | |||
| 3ea47d31a9 | |||
| 30c8cc4548 | |||
| 98586d2dd9 | |||
| c66d8ecca0 | |||
| db535f7e7d | |||
| 89f30d8c31 | |||
| 8bce432824 | |||
| f3539b2632 | |||
| 7a96c212f3 | |||
| 2d5dc54e0f | |||
| cef4c262e9 | |||
| 34309261a8 | |||
| 657eeb40c7 | |||
| b4938c2cc9 | |||
| 524fefc5cb | |||
| 73f13404bb | |||
| 5fc68cca83 | |||
| fcb7b8fa8d | |||
| a81e32ff95 | |||
| 4177c8e780 | |||
| c897052107 | |||
| 4d0efc7ea5 | |||
| a77842c9e3 | |||
| 0ded9db780 | |||
| 58d480fcb5 | |||
| 7154428d26 | |||
| 02a52d0169 | |||
| ec60764072 | |||
| 17d2fa5532 | |||
| 3ca8b534e8 | |||
| e88f7c5e46 | |||
| adcb90a7ac | |||
| 880057641e | |||
| 3ac5d7bd1f | |||
| 19cad00a00 | |||
| 1ea716a163 | |||
| 4551659c2a | |||
| c568bf8c24 | |||
| a412d91105 | |||
| ad01f23bbe | |||
| 656070b132 | |||
| c907f5ca36 | |||
| 94a13b2b54 | |||
| 169f11d9c7 | |||
| 816dc3af95 | |||
| b802863de5 | |||
| 8dc2a4b76c | |||
| beb1c6dbf2 | |||
| 3eb06916c7 | |||
| 30d0f84a4f | |||
| 251f2aa5db | |||
| 16d92ddb7c | |||
| e9d4fd8482 | |||
| 9fdcf5ab13 | |||
| a6e6de33c8 | |||
| 53c7fd4d81 | |||
| 2608ea4367 | |||
| d20ed4ad01 | |||
| eae2936449 | |||
| da8ad1f6ba | |||
| 5472347969 | |||
| a43f7a6926 | |||
| 47f917de0b | |||
| dbeb7edd7a | |||
| 6cede712f5 | |||
| d328739192 | |||
| d15c0bd962 | |||
| d53c794936 | |||
| e5dcb25a4a | |||
| f9002b4f43 | |||
| 0531508a75 | |||
| 174e85a20a | |||
| e9abb6f1e2 | |||
| 765964bdb2 | |||
| 5be44f9596 | |||
| 90efaaff92 | |||
| 99b554e7c3 | |||
| ac0ce8a6f6 | |||
| 9f9da885b7 | |||
| 584ead4a33 | |||
| f51fd2bf3e | |||
| c8378fd32d | |||
| d767214f10 | |||
| e027c504ae | |||
| 669a95d975 | |||
| a96df5d518 | |||
| c5c81735a0 | |||
| 5f86957556 | |||
| c933745707 | |||
| 6710fd18b3 | |||
| 1b78a71529 | |||
| 1909d3c94e | |||
| 10a107716c | |||
| 5445e6637b | |||
| 215b417da3 | |||
| 2380cd5cfb | |||
| 42661618dc | |||
| 8a7e901d7a | |||
| f8bea71716 | |||
| efcc0b7efc | |||
| 4d17e913b5 | |||
| b341934863 | |||
| 127f8ed3bb | |||
| 9dca80de18 | |||
| b0a6bcc055 | |||
| f0626e2eca | |||
| 979ab42c5a | |||
| e152e84ddc | |||
| 2605978889 | |||
| a2d30efaab | |||
| d3715d00ae | |||
| c37662dbe2 | |||
| 768ca1e73d | |||
| a20516850c | |||
| 7a203868ec | |||
| 43c3ce9a6e | |||
| 97dd76ea7b | |||
| 369e08844f | |||
| 48a5452a65 | |||
| c2f9340db6 | |||
| 35d762ccf9 | |||
| a6508e642a | |||
| a3e73797c2 | |||
| df7bf204ea | |||
| e16243ff55 | |||
| e23cbe67ad | |||
| 7702f2f74c | |||
| b457ef9a3f | |||
| a28ed6a977 | |||
| 0c73682cfc | |||
| 7e380b6cfb | |||
| 63b5257986 | |||
| acc4485e81 | |||
| 2c44999a31 | |||
| e59a48f9f4 | |||
| b86356b0cc | |||
| f6d7552b58 | |||
| 5bd88ac2e9 | |||
| 94fa5b59f3 | |||
| 7169480999 | |||
| c44ce0d77c | |||
| 7fd71a8408 | |||
| 68db721089 | |||
| a180e12bdf | |||
| f3a4a1b1be | |||
| 6977a8ecbc | |||
| d00f64e6ad | |||
| d5b3da6ba3 | |||
| c245318339 | |||
| b3b0fec2e1 | |||
| 9d571a4c71 | |||
| f283858490 | |||
| 76fe203767 | |||
| b9a47f2f50 | |||
| 27cb17c640 | |||
| ef8fb89eb3 | |||
| f1b045f8a8 | |||
| 050066132b | |||
| d0086de333 | |||
| 2a6e6a1e24 | |||
| 92689d084c | |||
| 00f314039d | |||
| fcb75e837d | |||
| 9fbea76b74 | |||
| b3ff120bcf | |||
| 9dea98f020 | |||
| 9eef389cdb | |||
| c4701d4e7a | |||
| 48903ca3a1 | |||
| 0c9fd4aef4 | |||
| b2af2e46ac | |||
| efc76a0683 | |||
| c4a553c166 | |||
| 69a00b0252 | |||
| 4257c08b43 | |||
| c9e5b92f79 | |||
| 99818c2ad8 | |||
| 99e3afabad | |||
| b9a0364b3b | |||
| 0b63efcd67 | |||
| b3060187ef | |||
| b6118e4c9f | |||
| 4135eb0110 | |||
| 936adcafa6 | |||
| ad62fc4aca | |||
| aaa12e51f0 | |||
| a440ddd7e7 | |||
| 0e571af728 | |||
| e46e983bb8 | |||
| 1ae9a57b57 | |||
| 5e80223e7a | |||
| 4d6174f5d8 | |||
| ce9a062bea | |||
| 4910dcfa96 | |||
| 744b45476c | |||
| ca43c6e69e |
@@ -10,7 +10,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-Linux-Ubuntu:
|
Build-Linux-Ubuntu:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.6.2
|
QT_VERSION: 6.6.2
|
||||||
@@ -20,6 +20,8 @@ jobs:
|
|||||||
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
@@ -90,6 +92,8 @@ jobs:
|
|||||||
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
@@ -156,6 +160,8 @@ jobs:
|
|||||||
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
@@ -190,7 +196,7 @@ jobs:
|
|||||||
- name: 'Install go'
|
- name: 'Install go'
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.22.1'
|
go-version: '1.24'
|
||||||
cache: false
|
cache: false
|
||||||
|
|
||||||
- name: 'Setup gomobile'
|
- name: 'Setup gomobile'
|
||||||
@@ -243,18 +249,33 @@ jobs:
|
|||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-MacOS:
|
Build-MacOS-old:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
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.4.3
|
||||||
QIF_VERSION: 4.6
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||||
|
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||||
|
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||||
|
|
||||||
|
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||||
|
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||||
|
|
||||||
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 }}
|
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
@@ -275,11 +296,6 @@ jobs:
|
|||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
|
||||||
run: |
|
|
||||||
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
|
||||||
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
|
||||||
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
|
||||||
|
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -293,14 +309,90 @@ jobs:
|
|||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
bash deploy/build_macos.sh -n
|
||||||
bash deploy/build_macos.sh
|
|
||||||
|
- name: 'Upload installer artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_old_installer
|
||||||
|
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_old_unpacked
|
||||||
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
|
||||||
|
Build-MacOS:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.8.0
|
||||||
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||||
|
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||||
|
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||||
|
|
||||||
|
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||||
|
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||||
|
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '16.2.0'
|
||||||
|
|
||||||
|
- name: 'Install Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: 'clang_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Setup ccache'
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
bash deploy/build_macos.sh -n
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_MacOS_installer
|
name: AmneziaVPN_MacOS_installer
|
||||||
path: AmneziaVPN.dmg
|
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
- name: 'Upload unpacked artifact'
|
||||||
@@ -310,6 +402,67 @@ 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.3
|
||||||
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '16.2.0'
|
||||||
|
|
||||||
|
- name: 'Install Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: 'clang_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Setup ccache'
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
bash deploy/build_macos_ne.sh
|
||||||
|
|
||||||
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_unpacked
|
||||||
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
@@ -317,13 +470,15 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-34
|
ANDROID_BUILD_PLATFORM: android-34
|
||||||
QT_VERSION: 6.7.3
|
QT_VERSION: 6.6.3
|
||||||
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 }}
|
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
@@ -332,7 +487,7 @@ jobs:
|
|||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
host: 'linux'
|
host: 'linux'
|
||||||
target: 'desktop'
|
target: 'desktop'
|
||||||
arch: 'linux_gcc_64'
|
arch: 'gcc_64'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
py7zrversion: '==0.22.*'
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ jobs:
|
|||||||
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_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
|
|||||||
@@ -1,64 +1,41 @@
|
|||||||
name: 'Upload a new version'
|
name: 'Upload a new version'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
tags:
|
inputs:
|
||||||
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
RELEASE_VERSION:
|
||||||
|
description: 'Release version (e.g. 1.2.3.4)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
Upload-S3:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: upload
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout CMakeLists.txt
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.ref_name }}
|
ref: ${{ inputs.RELEASE_VERSION }}
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
deploy/deploy_s3.sh
|
||||||
sparse-checkout-cone-mode: false
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
- name: Verify git tag
|
- name: Verify git tag
|
||||||
run: |
|
run: |
|
||||||
GIT_TAG=${{ github.ref_name }}
|
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
||||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
||||||
|
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
||||||
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
|
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
||||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
|
|
||||||
else
|
else
|
||||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
|
echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Download artifacts from the "${{ github.ref_name }}" tag
|
- name: Setup Rclone
|
||||||
uses: robinraju/release-downloader@v1.8
|
uses: AnimMouse/setup-rclone@v1
|
||||||
with:
|
with:
|
||||||
tag: ${{ github.ref_name }}
|
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||||
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
|
|
||||||
out-file-path: ${{ github.ref_name }}
|
|
||||||
|
|
||||||
- name: Upload beta version
|
- name: Send dist to S3
|
||||||
uses: jakejarvis/s3-sync-action@master
|
run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_VERSION }}
|
||||||
if: contains(github.event.base_ref, 'dev')
|
|
||||||
with:
|
|
||||||
args: --include "AmneziaVPN*" --delete
|
|
||||||
env:
|
|
||||||
AWS_S3_BUCKET: updates
|
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
|
||||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
|
||||||
SOURCE_DIR: ${{ github.ref_name }}
|
|
||||||
DEST_DIR: beta/${{ github.ref_name }}
|
|
||||||
|
|
||||||
- name: Upload stable version
|
|
||||||
uses: jakejarvis/s3-sync-action@master
|
|
||||||
if: contains(github.event.base_ref, 'master')
|
|
||||||
with:
|
|
||||||
args: --include "AmneziaVPN*" --delete
|
|
||||||
env:
|
|
||||||
AWS_S3_BUCKET: updates
|
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
|
||||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
|
||||||
SOURCE_DIR: ${{ github.ref_name }}
|
|
||||||
DEST_DIR: stable/${{ github.ref_name }}
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ deploy/build_32/*
|
|||||||
deploy/build_64/*
|
deploy/build_64/*
|
||||||
winbuild*.bat
|
winbuild*.bat
|
||||||
.cache/
|
.cache/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
|
||||||
# Qt-es
|
# Qt-es
|
||||||
@@ -134,3 +135,8 @@ out/
|
|||||||
|
|
||||||
# CMake files
|
# CMake files
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
|
|
||||||
|
ios-ne-build.sh
|
||||||
|
macos-ne-build.sh
|
||||||
|
macos-signed-build.sh
|
||||||
|
macos-with-sign-build.sh
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
[submodule "client/3rd-prebuilt"]
|
[submodule "client/3rd-prebuilt"]
|
||||||
path = client/3rd-prebuilt
|
path = client/3rd-prebuilt
|
||||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||||
|
branch = feature/special-handshake
|
||||||
[submodule "client/3rd/amneziawg-apple"]
|
[submodule "client/3rd/amneziawg-apple"]
|
||||||
path = client/3rd/amneziawg-apple
|
path = client/3rd/amneziawg-apple
|
||||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
set(AMNEZIAVPN_VERSION 4.8.11.0)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.4.3
|
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
@@ -11,7 +12,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 2080)
|
set(APP_ANDROID_VERSION_CODE 1095)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -31,13 +32,19 @@ 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)
|
||||||
|
if(IOS)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||||
|
elseif(MACOS_NE)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||||
|
else()
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
|
endif()
|
||||||
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)
|
||||||
|
|||||||
@@ -9,17 +9,17 @@
|
|||||||
### [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)
|
### [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?utm_source=github&utm_campaign=amnezia_website-readme-en) 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)
|
||||||
|
|
||||||
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
|
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror).
|
||||||
|
|
||||||
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
||||||
|
|
||||||
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,16 @@
|
|||||||
[](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.md) | Русский
|
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||||
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||||
|
|
||||||
[](https://amnezia.org)
|
[](https://amnezia.org)
|
||||||
|
|
||||||
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror).
|
||||||
|
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><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>
|
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><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)
|
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
|
- Классические 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.
|
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
|
||||||
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
||||||
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
|
- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
|
||||||
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
||||||
|
|
||||||
## Ссылки
|
## Ссылки
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
||||||
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
||||||
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
- [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_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_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_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
|
||||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в 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\)
|
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
|
||||||
|
|
||||||
## Технологии
|
## Технологии
|
||||||
@@ -80,8 +80,8 @@ git submodule update --init --recursive
|
|||||||
Проверьте папку deploy для скриптов сборки.
|
Проверьте папку deploy для скриптов сборки.
|
||||||
|
|
||||||
### Как собрать iOS-приложение из исходного кода на MacOS
|
### Как собрать iOS-приложение из исходного кода на MacOS
|
||||||
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
|
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
|
||||||
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||||
- MacOS
|
- MacOS
|
||||||
- iOS
|
- iOS
|
||||||
- Модуль совместимости с Qt 5
|
- Модуль совместимости с Qt 5
|
||||||
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
|||||||
export PATH=$(PATH):/path/to/GOPATH/bin
|
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||||
|
|
||||||
Если сборка завершится с ошибкой:
|
Если сборка завершится с ошибкой:
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
project(${PROJECT})
|
project(${PROJECT})
|
||||||
|
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
||||||
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||||
@@ -31,9 +30,8 @@ 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}")
|
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
||||||
|
|
||||||
if(IOS)
|
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
||||||
set(PACKAGES ${PACKAGES} Multimedia)
|
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
set(PACKAGES ${PACKAGES} Widgets)
|
set(PACKAGES ${PACKAGES} Widgets)
|
||||||
@@ -48,16 +46,15 @@ set(LIBS ${LIBS}
|
|||||||
Qt6::Core5Compat Qt6::Concurrent
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
if(IOS)
|
|
||||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
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)
|
||||||
|
target_include_directories(${PROJECT} PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||||
|
)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
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)
|
||||||
@@ -115,6 +112,15 @@ include_directories(
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
include_directories(mozilla)
|
include_directories(mozilla)
|
||||||
include_directories(mozilla/shared)
|
include_directories(mozilla/shared)
|
||||||
include_directories(mozilla/models)
|
include_directories(mozilla/models)
|
||||||
@@ -144,7 +150,7 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
cmake_policy(SET CMP0099 OLD)
|
cmake_policy(SET CMP0099 NEW)
|
||||||
cmake_policy(SET CMP0114 NEW)
|
cmake_policy(SET CMP0114 NEW)
|
||||||
|
|
||||||
if(NOT BUILD_OSX_APP_IDENTIFIER)
|
if(NOT BUILD_OSX_APP_IDENTIFIER)
|
||||||
@@ -163,7 +169,6 @@ if(APPLE)
|
|||||||
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
||||||
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})
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX AND NOT ANDROID)
|
if(LINUX AND NOT ANDROID)
|
||||||
@@ -171,8 +176,7 @@ 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))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
message("Client desktop build")
|
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -183,7 +187,9 @@ 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 MACOS_NE)
|
||||||
|
include(cmake/macos_ne.cmake)
|
||||||
|
elseif(APPLE)
|
||||||
include(cmake/osxtools.cmake)
|
include(cmake/osxtools.cmake)
|
||||||
include(cmake/macos.cmake)
|
include(cmake/macos.cmake)
|
||||||
endif()
|
endif()
|
||||||
@@ -204,7 +210,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>
|
||||||
@@ -219,7 +225,6 @@ if(NOT IOS AND NOT ANDROID)
|
|||||||
$<TARGET_FILE_DIR:${PROJECT}>
|
$<TARGET_FILE_DIR:${PROJECT}>
|
||||||
COMMAND_EXPAND_LISTS
|
COMMAND_EXPAND_LISTS
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QEvent>
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "ui/controllers/pageController.h"
|
#include "ui/controllers/pageController.h"
|
||||||
@@ -21,8 +22,12 @@
|
|||||||
#include "platforms/ios/QRCodeReaderBase.h"
|
#include "platforms/ios/QRCodeReaderBase.h"
|
||||||
|
|
||||||
#include "protocols/qml_register_protocols.h"
|
#include "protocols/qml_register_protocols.h"
|
||||||
|
#include <QtQuick/QQuickWindow> // for QQuickWindow
|
||||||
|
#include <QWindow> // for qobject_cast<QWindow*>
|
||||||
|
|
||||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv),
|
||||||
|
m_optAutostart({QStringLiteral("a"), QStringLiteral("autostart")}, QStringLiteral("System autostart")),
|
||||||
|
m_optCleanup ({QStringLiteral("c"), QStringLiteral("cleanup")}, QStringLiteral("Cleanup logs"))
|
||||||
{
|
{
|
||||||
setQuitOnLastWindowClosed(false);
|
setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
@@ -49,7 +54,6 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
|
|||||||
AmneziaApplication::~AmneziaApplication()
|
AmneziaApplication::~AmneziaApplication()
|
||||||
{
|
{
|
||||||
m_vpnConnectionThread.quit();
|
m_vpnConnectionThread.quit();
|
||||||
m_vpnConnectionThread.wait(3000);
|
|
||||||
|
|
||||||
if (m_engine) {
|
if (m_engine) {
|
||||||
QObject::disconnect(m_engine, 0, 0, 0);
|
QObject::disconnect(m_engine, 0, 0, 0);
|
||||||
@@ -64,14 +68,27 @@ void AmneziaApplication::init()
|
|||||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||||
[url](QObject *obj, const QUrl &objUrl) {
|
[this, url](QObject *obj, const QUrl &objUrl) {
|
||||||
if (!obj && url == objUrl)
|
if (!obj && url == objUrl) {
|
||||||
QCoreApplication::exit(-1);
|
QCoreApplication::exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// install filter on main window
|
||||||
|
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
|
||||||
|
win->installEventFilter(this);
|
||||||
|
win->show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||||
|
|
||||||
|
#ifdef MACOS_NE
|
||||||
|
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", true);
|
||||||
|
#else
|
||||||
|
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
||||||
|
#endif
|
||||||
|
|
||||||
m_vpnConnection.reset(new VpnConnection(m_settings));
|
m_vpnConnection.reset(new VpnConnection(m_settings));
|
||||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
@@ -94,7 +111,7 @@ void AmneziaApplication::init()
|
|||||||
Logger::setServiceLogsEnabled(enabled);
|
Logger::setServiceLogsEnabled(enabled);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN //TODO
|
#ifdef Q_OS_WIN //TODO
|
||||||
if (m_parser.isSet("a"))
|
if (m_parser.isSet(m_optAutostart))
|
||||||
m_coreController->pageController()->showOnStartup();
|
m_coreController->pageController()->showOnStartup();
|
||||||
else
|
else
|
||||||
emit m_coreController->pageController()->raiseMainWindow();
|
emit m_coreController->pageController()->raiseMainWindow();
|
||||||
@@ -162,15 +179,12 @@ bool AmneziaApplication::parseCommands()
|
|||||||
m_parser.addHelpOption();
|
m_parser.addHelpOption();
|
||||||
m_parser.addVersionOption();
|
m_parser.addVersionOption();
|
||||||
|
|
||||||
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
|
m_parser.addOption(m_optAutostart);
|
||||||
m_parser.addOption(c_autostart);
|
m_parser.addOption(m_optCleanup);
|
||||||
|
|
||||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
|
||||||
m_parser.addOption(c_cleanup);
|
|
||||||
|
|
||||||
m_parser.process(*this);
|
m_parser.process(*this);
|
||||||
|
|
||||||
if (m_parser.isSet(c_cleanup)) {
|
if (m_parser.isSet(m_optCleanup)) {
|
||||||
Logger::cleanUp();
|
Logger::cleanUp();
|
||||||
QTimer::singleShot(100, this, [this] { quit(); });
|
QTimer::singleShot(100, this, [this] { quit(); });
|
||||||
exec();
|
exec();
|
||||||
@@ -179,9 +193,8 @@ 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);
|
||||||
|
|
||||||
@@ -198,6 +211,22 @@ void AmneziaApplication::startLocalServer()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::Close) {
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
|
quit();
|
||||||
|
#else
|
||||||
|
if (m_coreController && m_coreController->pageController()) {
|
||||||
|
m_coreController->pageController()->hideMainWindow();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true; // eat the close
|
||||||
|
}
|
||||||
|
// call base QObject::eventFilter
|
||||||
|
return QObject::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||||
{
|
{
|
||||||
return m_engine;
|
return m_engine;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
void loadFonts();
|
void loadFonts();
|
||||||
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
|
||||||
|
|
||||||
@@ -56,10 +56,15 @@ private:
|
|||||||
|
|
||||||
QCommandLineParser m_parser;
|
QCommandLineParser m_parser;
|
||||||
|
|
||||||
|
QCommandLineOption m_optAutostart;
|
||||||
|
QCommandLineOption m_optCleanup;
|
||||||
|
|
||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||||
QThread m_vpnConnectionThread;
|
QThread m_vpnConnectionThread;
|
||||||
|
|
||||||
QNetworkAccessManager *m_nam;
|
QNetworkAccessManager *m_nam;
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AMNEZIA_APPLICATION_H
|
#endif // AMNEZIA_APPLICATION_H
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="org.amnezia.vpn"
|
||||||
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
||||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<uses-sdk android:maxSdkVersion="25" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
@@ -67,6 +70,9 @@
|
|||||||
android:name="android.app.lib_name"
|
android:name="android.app.lib_name"
|
||||||
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
|
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.extract_android_style"
|
||||||
|
android:value="minimal" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
|
|||||||
# For development copy and set local values for these parameters in local.properties
|
# For development copy and set local values for these parameters in local.properties
|
||||||
#androidCompileSdkVersion=android-34
|
#androidCompileSdkVersion=android-34
|
||||||
#androidBuildToolsVersion=34.0.0
|
#androidBuildToolsVersion=34.0.0
|
||||||
#qtMinSdkVersion=26
|
#qtMinSdkVersion=24
|
||||||
#qtTargetSdkVersion=34
|
#qtTargetSdkVersion=34
|
||||||
#androidNdkVersion=26.1.10909125
|
#androidNdkVersion=26.1.10909125
|
||||||
#qtTargetAbiList=x86_64
|
#qtTargetAbiList=x86_64
|
||||||
|
|||||||
@@ -183,14 +183,6 @@ class OpenVpnClient(
|
|||||||
// Never called more than once per tun_builder session.
|
// Never called more than once per tun_builder session.
|
||||||
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
||||||
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
|
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
try {
|
|
||||||
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Could not set proxy: ${e.message}")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,15 +112,10 @@ abstract class Protocol {
|
|||||||
if (include) {
|
if (include) {
|
||||||
Log.d(TAG, "addRoute: $inetNetwork")
|
Log.d(TAG, "addRoute: $inetNetwork")
|
||||||
vpnBuilder.addRoute(inetNetwork)
|
vpnBuilder.addRoute(inetNetwork)
|
||||||
} else {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
Log.d(TAG, "excludeRoute: $inetNetwork")
|
|
||||||
vpnBuilder.excludeRoute(inetNetwork)
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
|
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (app in config.includedApplications) {
|
for (app in config.includedApplications) {
|
||||||
Log.d(TAG, "addAllowedApplication")
|
Log.d(TAG, "addAllowedApplication")
|
||||||
@@ -135,13 +130,6 @@ abstract class Protocol {
|
|||||||
Log.d(TAG, "setMtu: ${config.mtu}")
|
Log.d(TAG, "setMtu: ${config.mtu}")
|
||||||
vpnBuilder.setMtu(config.mtu)
|
vpnBuilder.setMtu(config.mtu)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
config.httpProxy?.let {
|
|
||||||
Log.d(TAG, "setHttpProxy: $it")
|
|
||||||
vpnBuilder.setHttpProxy(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.allowAllAF) {
|
if (config.allowAllAF) {
|
||||||
Log.d(TAG, "allowFamily")
|
Log.d(TAG, "allowFamily")
|
||||||
vpnBuilder.allowFamily(OsConstants.AF_INET)
|
vpnBuilder.allowFamily(OsConstants.AF_INET)
|
||||||
@@ -151,8 +139,6 @@ abstract class Protocol {
|
|||||||
Log.d(TAG, "setBlocking: ${config.blockingMode}")
|
Log.d(TAG, "setBlocking: ${config.blockingMode}")
|
||||||
vpnBuilder.setBlocking(config.blockingMode)
|
vpnBuilder.setBlocking(config.blockingMode)
|
||||||
vpnBuilder.setUnderlyingNetworks(null)
|
vpnBuilder.setUnderlyingNetworks(null)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
|
||||||
vpnBuilder.setMetered(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ open class ProtocolConfig protected constructor(
|
|||||||
}
|
}
|
||||||
// for older versions of Android, build a list of subnets without excluded routes
|
// for older versions of Android, build a list of subnets without excluded routes
|
||||||
// and add them to routes
|
// and add them to routes
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && routes.any { !it.include }) {
|
if (routes.any { !it.include }) {
|
||||||
val ipRangeSet = IpRangeSet()
|
val ipRangeSet = IpRangeSet()
|
||||||
routes.forEach {
|
routes.forEach {
|
||||||
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
|
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
|
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ package org.amnezia.vpn
|
|||||||
import android.Manifest
|
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.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.EXTRA_MIME_TYPES
|
import android.content.Intent.EXTRA_MIME_TYPES
|
||||||
@@ -77,7 +75,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
private var isWaitingStatus = true
|
private var isWaitingStatus = true
|
||||||
private var isServiceConnected = false
|
private var isServiceConnected = false
|
||||||
private var isInBoundState = false
|
private var isInBoundState = false
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
|
||||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||||
private var pfd: ParcelFileDescriptor? = null
|
private var pfd: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
@@ -186,7 +183,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
doBindService()
|
doBindService()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
registerBroadcastReceivers()
|
|
||||||
intent?.let(::processIntent)
|
intent?.let(::processIntent)
|
||||||
runBlocking { vpnProto = proto.await() }
|
runBlocking { vpnProto = proto.await() }
|
||||||
}
|
}
|
||||||
@@ -202,26 +198,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerBroadcastReceivers() {
|
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
registerBroadcastReceiver(
|
|
||||||
arrayOf(
|
|
||||||
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
|
|
||||||
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Log.v(
|
|
||||||
TAG, "Notification state changed: ${it?.action}, blocked = " +
|
|
||||||
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
|
|
||||||
)
|
|
||||||
mainScope.launch {
|
|
||||||
qtInitialized.await()
|
|
||||||
QtAndroidController.onNotificationStateChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
Log.v(TAG, "onNewIntent: $intent")
|
Log.v(TAG, "onNewIntent: $intent")
|
||||||
@@ -267,8 +243,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(TAG, "Destroy Amnezia activity")
|
Log.d(TAG, "Destroy Amnezia activity")
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
|
||||||
notificationStateReceiver = null
|
|
||||||
mainScope.cancel()
|
mainScope.cancel()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
@@ -747,7 +721,7 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun isNotificationPermissionGranted(): Boolean = applicationContext.isNotificationPermissionGranted()
|
fun isNotificationPermissionGranted(): Boolean = true
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun requestNotificationPermission() {
|
fun requestNotificationPermission() {
|
||||||
@@ -847,67 +821,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
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
|
|
||||||
// also disable right-click, as it causes the application to crash
|
|
||||||
private var lastButtonState = 0
|
|
||||||
private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
|
|
||||||
downTime,
|
|
||||||
eventTime,
|
|
||||||
action,
|
|
||||||
pointerCount,
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerProperties().apply {
|
|
||||||
getPointerProperties(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerCoords().apply {
|
|
||||||
getPointerCoords(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
metaState,
|
|
||||||
MotionEvent.BUTTON_PRIMARY,
|
|
||||||
xPrecision,
|
|
||||||
yPrecision,
|
|
||||||
deviceId,
|
|
||||||
edgeFlags,
|
|
||||||
source,
|
|
||||||
flags
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
|
|
||||||
when (ev.action) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
lastButtonState = ev.buttonState
|
|
||||||
if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
when (lastButtonState) {
|
|
||||||
MotionEvent.BUTTON_SECONDARY -> return true
|
|
||||||
MotionEvent.BUTTON_PRIMARY -> {
|
|
||||||
val modEvent = ev.fixCopy()
|
|
||||||
return superDispatch(modEvent).apply { modEvent.recycle() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return superDispatch(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
|
||||||
Log.v(TAG, "dispatchTouch: $ev")
|
|
||||||
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
|
||||||
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
|
||||||
}
|
|
||||||
return super.dispatchTouchEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
|
|
||||||
ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
|
|
||||||
return super.dispatchTrackballEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils methods
|
* Utils methods
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
import android.service.quicksettings.Tile
|
import android.service.quicksettings.Tile
|
||||||
@@ -148,7 +145,8 @@ class AmneziaTileService : TileService() {
|
|||||||
Intent(this, AmneziaActivity::class.java).apply {
|
Intent(this, AmneziaActivity::class.java).apply {
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}.also {
|
}.also {
|
||||||
startActivityAndCollapseCompat(it)
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityAndCollapse(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,7 +190,8 @@ class AmneziaTileService : TileService() {
|
|||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
putExtra(EXTRA_PROTOCOL, vpnProto)
|
putExtra(EXTRA_PROTOCOL, vpnProto)
|
||||||
}.also {
|
}.also {
|
||||||
startActivityAndCollapseCompat(it)
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityAndCollapse(it)
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@@ -216,23 +215,6 @@ class AmneziaTileService : TileService() {
|
|||||||
|
|
||||||
private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT)
|
private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT)
|
||||||
|
|
||||||
@SuppressLint("StartActivityAndCollapseDeprecated")
|
|
||||||
private fun startActivityAndCollapseCompat(intent: Intent) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
startActivityAndCollapse(
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
applicationContext,
|
|
||||||
0,
|
|
||||||
intent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
startActivityAndCollapse(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateVpnState(state: ProtocolState) =
|
private fun updateVpnState(state: ProtocolState) =
|
||||||
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
|
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
|
||||||
|
|
||||||
@@ -249,17 +231,14 @@ class AmneziaTileService : TileService() {
|
|||||||
when (val protocolState = vpnState.protocolState) {
|
when (val protocolState = vpnState.protocolState) {
|
||||||
CONNECTED -> {
|
CONNECTED -> {
|
||||||
state = Tile.STATE_ACTIVE
|
state = Tile.STATE_ACTIVE
|
||||||
subtitleCompat = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DISCONNECTED, UNKNOWN -> {
|
DISCONNECTED, UNKNOWN -> {
|
||||||
state = Tile.STATE_INACTIVE
|
state = Tile.STATE_INACTIVE
|
||||||
subtitleCompat = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTING, DISCONNECTING, RECONNECTING -> {
|
CONNECTING, DISCONNECTING, RECONNECTING -> {
|
||||||
state = Tile.STATE_UNAVAILABLE
|
state = Tile.STATE_UNAVAILABLE
|
||||||
subtitleCompat = getString(protocolState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateTile()
|
updateTile()
|
||||||
@@ -267,17 +246,4 @@ class AmneziaTileService : TileService() {
|
|||||||
// double update to fix weird visual glitches
|
// double update to fix weird visual glitches
|
||||||
tile.updateTile()
|
tile.updateTile()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var Tile.subtitleCompat: CharSequence?
|
|
||||||
set(value) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
this.subtitle = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
return this.subtitle
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,10 @@ package org.amnezia.vpn
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
|
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
|
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
|
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@@ -104,7 +100,6 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
private lateinit var networkState: NetworkState
|
private lateinit var networkState: NetworkState
|
||||||
private lateinit var trafficStats: TrafficStats
|
private lateinit var trafficStats: TrafficStats
|
||||||
private var controlReceiver: BroadcastReceiver? = null
|
private var controlReceiver: BroadcastReceiver? = null
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
|
||||||
private var screenOnReceiver: BroadcastReceiver? = null
|
private var screenOnReceiver: BroadcastReceiver? = null
|
||||||
private var screenOffReceiver: BroadcastReceiver? = null
|
private var screenOffReceiver: BroadcastReceiver? = null
|
||||||
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
|
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
|
||||||
@@ -189,16 +184,6 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
Messenger(actionMessageHandler)
|
Messenger(actionMessageHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification setup
|
|
||||||
*/
|
|
||||||
private val foregroundServiceTypeCompat
|
|
||||||
get() = when {
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> FOREGROUND_SERVICE_TYPE_MANIFEST
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
|
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,7 +217,7 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
ServiceCompat.startForeground(
|
ServiceCompat.startForeground(
|
||||||
this, NOTIFICATION_ID,
|
this, NOTIFICATION_ID,
|
||||||
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
|
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
|
||||||
foregroundServiceTypeCompat
|
0
|
||||||
)
|
)
|
||||||
return START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
}
|
}
|
||||||
@@ -309,23 +294,6 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
registerBroadcastReceiver(
|
|
||||||
arrayOf(
|
|
||||||
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
|
|
||||||
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
|
|
||||||
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
|
|
||||||
if (state == false) {
|
|
||||||
enableNotification()
|
|
||||||
} else {
|
|
||||||
disableNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
|
|
||||||
registerScreenStateBroadcastReceivers()
|
registerScreenStateBroadcastReceivers()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,10 +321,8 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
private fun unregisterBroadcastReceivers() {
|
private fun unregisterBroadcastReceivers() {
|
||||||
Log.d(TAG, "Unregister broadcast receivers")
|
Log.d(TAG, "Unregister broadcast receivers")
|
||||||
unregisterBroadcastReceiver(controlReceiver)
|
unregisterBroadcastReceiver(controlReceiver)
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
|
||||||
unregisterScreenStateBroadcastReceivers()
|
unregisterScreenStateBroadcastReceivers()
|
||||||
controlReceiver = null
|
controlReceiver = null
|
||||||
notificationStateReceiver = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.Manifest.permission
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.app.NotificationChannelCompat.Builder
|
import androidx.core.app.NotificationChannelCompat.Builder
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.Action
|
import androidx.core.app.NotificationCompat.Action
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import org.amnezia.vpn.protocol.ProtocolState
|
import org.amnezia.vpn.protocol.ProtocolState
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
@@ -85,28 +81,18 @@ class ServiceNotification(private val context: Context) {
|
|||||||
.setSubText(getSpeedString(speed))
|
.setSubText(getSpeedString(speed))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun isNotificationEnabled(): Boolean {
|
fun isNotificationEnabled(): Boolean = notificationManager.areNotificationsEnabled()
|
||||||
if (!context.isNotificationPermissionGranted()) return false
|
|
||||||
if (!notificationManager.areNotificationsEnabled()) return false
|
|
||||||
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let {
|
|
||||||
it.importance != NotificationManager.IMPORTANCE_NONE
|
|
||||||
} ?: true
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
||||||
if (context.isNotificationPermissionGranted()) {
|
|
||||||
Log.v(TAG, "Update notification: $serverName, $state")
|
Log.v(TAG, "Update notification: $serverName, $state")
|
||||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun updateSpeed(speed: TrafficData) {
|
fun updateSpeed(speed: TrafficData) {
|
||||||
if (context.isNotificationPermissionGranted()) {
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSpeedString(traffic: TrafficData) =
|
private fun getSpeedString(traffic: TrafficData) =
|
||||||
if (traffic == TrafficData.ZERO) zeroSpeed
|
if (traffic == TrafficData.ZERO) zeroSpeed
|
||||||
@@ -166,8 +152,3 @@ class ServiceNotification(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.isNotificationPermissionGranted(): Boolean =
|
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
|
||||||
ContextCompat.checkSelfPermission(this, permission.POST_NOTIFICATIONS) ==
|
|
||||||
PackageManager.PERMISSION_GRANTED
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.content.Intent
|
|||||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -31,12 +30,9 @@ class VpnRequestActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d(TAG, "Start request activity")
|
Log.d(TAG, "Start request activity")
|
||||||
vpnProto = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
intent.extras?.getSerializable(EXTRA_PROTOCOL, VpnProto::class.java)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
|
vpnProto = intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
|
||||||
}
|
|
||||||
val requestIntent = VpnService.prepare(applicationContext)
|
val requestIntent = VpnService.prepare(applicationContext)
|
||||||
if (requestIntent != null) {
|
if (requestIntent != null) {
|
||||||
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package org.amnezia.vpn.util
|
package org.amnezia.vpn.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.icu.text.DateFormat
|
||||||
|
import android.icu.text.SimpleDateFormat
|
||||||
|
import android.icu.util.TimeZone
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -8,8 +11,8 @@ import java.io.IOException
|
|||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import java.nio.channels.FileChannel
|
import java.nio.channels.FileChannel
|
||||||
import java.nio.channels.FileLock
|
import java.nio.channels.FileLock
|
||||||
import java.time.LocalDateTime
|
import java.util.Date
|
||||||
import java.time.format.DateTimeFormatter
|
import java.util.Locale
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import org.amnezia.vpn.util.Log.Priority.D
|
import org.amnezia.vpn.util.Log.Priority.D
|
||||||
import org.amnezia.vpn.util.Log.Priority.E
|
import org.amnezia.vpn.util.Log.Priority.E
|
||||||
@@ -37,7 +40,11 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
|
|||||||
* | | | create a report and/or terminate the process |
|
* | | | create a report and/or terminate the process |
|
||||||
*/
|
*/
|
||||||
object Log {
|
object Log {
|
||||||
private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
|
private val dateTimeFormat = object : ThreadLocal<DateFormat>() {
|
||||||
|
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US).apply {
|
||||||
|
timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var logDir: File
|
private lateinit var logDir: File
|
||||||
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
||||||
@@ -135,8 +142,8 @@ object Log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||||
val date = LocalDateTime.now().format(dateTimeFormat)
|
val utcDate = dateTimeFormat.get()?.format(Date())
|
||||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||||
"$tag: $msg\n"
|
"$tag: $msg\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import android.net.NetworkCapabilities
|
|||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
import android.os.Build
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
|
|
||||||
private const val TAG = "NetworkState"
|
private const val TAG = "NetworkState"
|
||||||
@@ -47,8 +45,10 @@ class NetworkState(
|
|||||||
|
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
||||||
|
handler.post {
|
||||||
checkNetworkState(network, networkCapabilities)
|
checkNetworkState(network, networkCapabilities)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
if (currentNetwork == null) {
|
if (currentNetwork == null) {
|
||||||
@@ -76,33 +76,10 @@ class NetworkState(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun bindNetworkListener() {
|
fun bindNetworkListener() {
|
||||||
if (isListenerBound) return
|
if (isListenerBound) return
|
||||||
Log.d(TAG, "Bind network listener")
|
Log.d(TAG, "Bind network listener")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
connectivityManager.requestNetwork(networkRequest, networkCallback)
|
||||||
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
|
|
||||||
} else {
|
|
||||||
val numberAttempts = 300
|
|
||||||
var attemptCount = 0
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
|
|
||||||
break
|
|
||||||
} catch (e: SecurityException) {
|
|
||||||
Log.e(TAG, "Failed to bind network listener: $e")
|
|
||||||
// Android 11 bug: https://issuetracker.google.com/issues/175055271
|
|
||||||
if (e.message?.startsWith("Package android does not belong to") == true) {
|
|
||||||
if (++attemptCount > numberAttempts) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
delay(1000)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isListenerBound = true
|
isListenerBound = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.amnezia.vpn.util.net
|
package org.amnezia.vpn.util.net
|
||||||
|
|
||||||
import android.net.TrafficStats
|
import android.net.TrafficStats
|
||||||
import android.os.Build
|
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
@@ -17,13 +16,7 @@ class TrafficStats {
|
|||||||
private var lastTrafficData = TrafficData.ZERO
|
private var lastTrafficData = TrafficData.ZERO
|
||||||
private var lastTimestamp = 0L
|
private var lastTimestamp = 0L
|
||||||
|
|
||||||
private val getTrafficDataCompat: () -> TrafficData =
|
private val getTrafficDataCompat: () -> TrafficData = run {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
val iface = "tun0"
|
|
||||||
fun(): TrafficData {
|
|
||||||
return TrafficData(TrafficStats.getRxBytes(iface), TrafficStats.getTxBytes(iface))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val uid = Process.myUid()
|
val uid = Process.myUid()
|
||||||
fun(): TrafficData {
|
fun(): TrafficData {
|
||||||
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
|
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
|
||||||
|
|||||||
@@ -120,10 +120,21 @@ open class Wireguard : Protocol() {
|
|||||||
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
||||||
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
||||||
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
||||||
|
configData.optStringOrNull("S3")?.let { setS3(it.toInt()) }
|
||||||
|
configData.optStringOrNull("S4")?.let { setS4(it.toInt()) }
|
||||||
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
||||||
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
||||||
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
||||||
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
||||||
|
configData.optStringOrNull("I1")?.let { setI1(it) }
|
||||||
|
configData.optStringOrNull("I2")?.let { setI2(it) }
|
||||||
|
configData.optStringOrNull("I3")?.let { setI3(it) }
|
||||||
|
configData.optStringOrNull("I4")?.let { setI4(it) }
|
||||||
|
configData.optStringOrNull("I5")?.let { setI5(it) }
|
||||||
|
configData.optStringOrNull("J1")?.let { setJ1(it) }
|
||||||
|
configData.optStringOrNull("J2")?.let { setJ2(it) }
|
||||||
|
configData.optStringOrNull("J3")?.let { setJ3(it) }
|
||||||
|
configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||||
|
|||||||
@@ -20,10 +20,21 @@ open class WireguardConfig protected constructor(
|
|||||||
val jmax: Int?,
|
val jmax: Int?,
|
||||||
val s1: Int?,
|
val s1: Int?,
|
||||||
val s2: Int?,
|
val s2: Int?,
|
||||||
|
val s3: Int?,
|
||||||
|
val s4: Int?,
|
||||||
val h1: Long?,
|
val h1: Long?,
|
||||||
val h2: Long?,
|
val h2: Long?,
|
||||||
val h3: Long?,
|
val h3: Long?,
|
||||||
val h4: Long?
|
val h4: Long?,
|
||||||
|
var i1: String?,
|
||||||
|
var i2: String?,
|
||||||
|
var i3: String?,
|
||||||
|
var i4: String?,
|
||||||
|
var i5: String?,
|
||||||
|
var j1: String?,
|
||||||
|
var j2: String?,
|
||||||
|
var j3: String?,
|
||||||
|
var itime: Int?
|
||||||
) : ProtocolConfig(protocolConfigBuilder) {
|
) : ProtocolConfig(protocolConfigBuilder) {
|
||||||
|
|
||||||
protected constructor(builder: Builder) : this(
|
protected constructor(builder: Builder) : this(
|
||||||
@@ -39,10 +50,21 @@ open class WireguardConfig protected constructor(
|
|||||||
builder.jmax,
|
builder.jmax,
|
||||||
builder.s1,
|
builder.s1,
|
||||||
builder.s2,
|
builder.s2,
|
||||||
|
builder.s3,
|
||||||
|
builder.s4,
|
||||||
builder.h1,
|
builder.h1,
|
||||||
builder.h2,
|
builder.h2,
|
||||||
builder.h3,
|
builder.h3,
|
||||||
builder.h4
|
builder.h4,
|
||||||
|
builder.i1,
|
||||||
|
builder.i2,
|
||||||
|
builder.i3,
|
||||||
|
builder.i4,
|
||||||
|
builder.i5,
|
||||||
|
builder.j1,
|
||||||
|
builder.j2,
|
||||||
|
builder.j3,
|
||||||
|
builder.itime
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
||||||
@@ -61,10 +83,21 @@ open class WireguardConfig protected constructor(
|
|||||||
appendLine("jmax=$jmax")
|
appendLine("jmax=$jmax")
|
||||||
appendLine("s1=$s1")
|
appendLine("s1=$s1")
|
||||||
appendLine("s2=$s2")
|
appendLine("s2=$s2")
|
||||||
|
s3?.let { appendLine("s3=$it") }
|
||||||
|
s4?.let { appendLine("s4=$it") }
|
||||||
appendLine("h1=$h1")
|
appendLine("h1=$h1")
|
||||||
appendLine("h2=$h2")
|
appendLine("h2=$h2")
|
||||||
appendLine("h3=$h3")
|
appendLine("h3=$h3")
|
||||||
appendLine("h4=$h4")
|
appendLine("h4=$h4")
|
||||||
|
i1?.let { appendLine("i1=$it") }
|
||||||
|
i2?.let { appendLine("i2=$it") }
|
||||||
|
i3?.let { appendLine("i3=$it") }
|
||||||
|
i4?.let { appendLine("i4=$it") }
|
||||||
|
i5?.let { appendLine("i5=$it") }
|
||||||
|
j1?.let { appendLine("j1=$it") }
|
||||||
|
j2?.let { appendLine("j2=$it") }
|
||||||
|
j3?.let { appendLine("j3=$it") }
|
||||||
|
itime?.let { appendLine("itime=$it") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +150,21 @@ open class WireguardConfig protected constructor(
|
|||||||
internal var jmax: Int? = null
|
internal var jmax: Int? = null
|
||||||
internal var s1: Int? = null
|
internal var s1: Int? = null
|
||||||
internal var s2: Int? = null
|
internal var s2: Int? = null
|
||||||
|
internal var s3: Int? = null
|
||||||
|
internal var s4: Int? = null
|
||||||
internal var h1: Long? = null
|
internal var h1: Long? = null
|
||||||
internal var h2: Long? = null
|
internal var h2: Long? = null
|
||||||
internal var h3: Long? = null
|
internal var h3: Long? = null
|
||||||
internal var h4: Long? = null
|
internal var h4: Long? = null
|
||||||
|
internal var i1: String? = null
|
||||||
|
internal var i2: String? = null
|
||||||
|
internal var i3: String? = null
|
||||||
|
internal var i4: String? = null
|
||||||
|
internal var i5: String? = null
|
||||||
|
internal var j1: String? = null
|
||||||
|
internal var j2: String? = null
|
||||||
|
internal var j3: String? = null
|
||||||
|
internal var itime: Int? = null
|
||||||
|
|
||||||
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
||||||
|
|
||||||
@@ -139,10 +183,21 @@ open class WireguardConfig protected constructor(
|
|||||||
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
||||||
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
||||||
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
||||||
|
fun setS3(s3: Int) = apply { this.s3 = s3 }
|
||||||
|
fun setS4(s4: Int) = apply { this.s4 = s4 }
|
||||||
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
||||||
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
||||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||||
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
||||||
|
fun setI1(i1: String) = apply { this.i1 = i1 }
|
||||||
|
fun setI2(i2: String) = apply { this.i2 = i2 }
|
||||||
|
fun setI3(i3: String) = apply { this.i3 = i3 }
|
||||||
|
fun setI4(i4: String) = apply { this.i4 = i4 }
|
||||||
|
fun setI5(i5: String) = apply { this.i5 = i5 }
|
||||||
|
fun setJ1(j1: String) = apply { this.j1 = j1 }
|
||||||
|
fun setJ2(j2: String) = apply { this.j2 = j2 }
|
||||||
|
fun setJ3(j3: String) = apply { this.j3 = j3 }
|
||||||
|
fun setItime(itime: Int) = apply { this.itime = itime }
|
||||||
|
|
||||||
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,15 @@ 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)
|
||||||
|
if(MACOS_NE)
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
|
||||||
|
else()
|
||||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
||||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
||||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||||
|
endif()
|
||||||
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")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
||||||
|
|
||||||
set(APP_ANDROID_MIN_SDK 26)
|
set(APP_ANDROID_MIN_SDK 24)
|
||||||
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
||||||
"The minimum API level supported by the application or library" FORCE)
|
"The minimum API level supported by the application or library" FORCE)
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -76,8 +77,22 @@ 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_STYLE Automatic
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(DEFINED DEPLOY)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
@@ -121,10 +136,21 @@ set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
|||||||
add_subdirectory(ios/networkextension)
|
add_subdirectory(ios/networkextension)
|
||||||
add_dependencies(${PROJECT} networkextension)
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
set(OPENVPN_FRAMEWORK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios")
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
set(OPENVPN_EMBEDDED_FRAMEWORKS
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNAdapter.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNClient.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/mbedTLS.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/LZ4.framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
|
||||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
|
||||||
|
|
||||||
|
foreach(_framework ${OPENVPN_EMBEDDED_FRAMEWORKS})
|
||||||
|
target_link_libraries(networkextension PRIVATE "${_framework}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON)
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ set(LIBS ${LIBS}
|
|||||||
${FW_SECURITY}
|
${FW_SECURITY}
|
||||||
${FW_COREWLAN}
|
${FW_COREWLAN}
|
||||||
${FW_NETWORK}
|
${FW_NETWORK}
|
||||||
${FW_USERNOTIFICATIONS}
|
${FW_USER_NOTIFICATIONS}
|
||||||
${FW_NETWORK_EXTENSION}
|
${FW_NETWORK_EXTENSION}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
@@ -31,6 +35,8 @@ set(SOURCES ${SOURCES}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
@@ -49,4 +55,3 @@ execute_process(
|
|||||||
)
|
)
|
||||||
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
message("Client ==> MacOS NE build")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
|
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 Widgets)
|
||||||
|
# Link Qt Widgets for QWidget, QMenu, QAction etc.
|
||||||
|
set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets)
|
||||||
|
|
||||||
|
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||||
|
find_library(FW_AVFOUNDATION AVFoundation)
|
||||||
|
find_library(FW_FOUNDATION Foundation)
|
||||||
|
find_library(FW_STOREKIT StoreKit)
|
||||||
|
find_library(FW_SERVICEMGMT ServiceManagement)
|
||||||
|
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||||
|
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${FW_AUTHENTICATIONSERVICES}
|
||||||
|
${FW_AVFOUNDATION}
|
||||||
|
${FW_FOUNDATION}
|
||||||
|
${FW_STOREKIT}
|
||||||
|
${FW_SERVICEMGMT}
|
||||||
|
${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}
|
||||||
|
${Qt6Widgets_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_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||||
|
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
||||||
|
XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
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} AmneziaVPNNetworkExtension)
|
||||||
|
|
||||||
|
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-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
|
||||||
|
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||||
|
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
|
||||||
|
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
|
||||||
|
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
|
||||||
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMENT "Signing OpenVPNAdapter framework"
|
||||||
|
)
|
||||||
@@ -39,7 +39,7 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
)
|
)
|
||||||
@@ -89,12 +89,26 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Include native macOS platform helpers (dock/status-item)
|
||||||
|
if(APPLE AND NOT IOS)
|
||||||
|
list(APPEND HEADERS
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/macos_util.h
|
||||||
|
)
|
||||||
|
list(APPEND SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
|
||||||
|
${CLIENT_ROOT_DIR}/ui/macos_util.mm
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "awg_configurator.h"
|
#include "awg_configurator.h"
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@@ -39,6 +40,20 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
|
|||||||
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
||||||
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
||||||
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
||||||
|
|
||||||
|
// jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
|
||||||
|
// jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
|
||||||
|
|
||||||
|
// jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
|
||||||
|
// jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
|
||||||
|
// jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
|
||||||
|
// jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
|
||||||
|
// jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
|
||||||
|
// jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1);
|
||||||
|
// jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2);
|
||||||
|
// jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3);
|
||||||
|
// jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout);
|
||||||
|
|
||||||
jsonConfig[config_key::mtu] =
|
jsonConfig[config_key::mtu] =
|
||||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "core/networkUtilities.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"
|
||||||
#include "core/server_defs.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <openssl/rsa.h>
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
|
|
||||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||||
QObject *parent)
|
QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(settings, serverController, parent)
|
||||||
@@ -82,12 +83,30 @@ QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials,
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto sanitizeStaticKey = [](const QString &key) {
|
||||||
|
QStringList lines = key.split('\n');
|
||||||
|
QStringList filtered;
|
||||||
|
filtered.reserve(lines.size());
|
||||||
|
for (const QString &line : lines) {
|
||||||
|
const QString trimmed = line.trimmed();
|
||||||
|
if (trimmed.startsWith('#')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filtered.append(line);
|
||||||
|
}
|
||||||
|
QString result = filtered.join('\n');
|
||||||
|
if (!result.endsWith('\n')) {
|
||||||
|
result.append('\n');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
config.replace("$OPENVPN_CA_CERT", connData.caCert);
|
config.replace("$OPENVPN_CA_CERT", connData.caCert);
|
||||||
config.replace("$OPENVPN_CLIENT_CERT", connData.clientCert);
|
config.replace("$OPENVPN_CLIENT_CERT", connData.clientCert);
|
||||||
config.replace("$OPENVPN_PRIV_KEY", connData.privKey);
|
config.replace("$OPENVPN_PRIV_KEY", connData.privKey);
|
||||||
|
|
||||||
if (config.contains("$OPENVPN_TA_KEY")) {
|
if (config.contains("$OPENVPN_TA_KEY")) {
|
||||||
config.replace("$OPENVPN_TA_KEY", connData.taKey);
|
config.replace("$OPENVPN_TA_KEY", sanitizeStaticKey(connData.taKey));
|
||||||
} else {
|
} else {
|
||||||
config.replace("<tls-auth>", "");
|
config.replace("<tls-auth>", "");
|
||||||
config.replace("</tls-auth>", "");
|
config.replace("</tls-auth>", "");
|
||||||
@@ -117,22 +136,22 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
|
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||||
|
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
||||||
|
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
||||||
|
config.replace(dnsRegex, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
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)
|
|
||||||
// Prevent ipv6 leak
|
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
|
||||||
#endif
|
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
|
|
||||||
// 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");
|
|
||||||
#endif
|
#endif
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
@@ -166,10 +185,15 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
|
|||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
|
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||||
|
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
||||||
|
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
||||||
|
config.replace(dnsRegex, "");
|
||||||
|
}
|
||||||
|
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 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("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|
||||||
// remove block-outside-dns for all exported configs
|
// remove block-outside-dns for all exported configs
|
||||||
|
|||||||
@@ -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_MACX) && !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
@@ -19,13 +20,17 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
||||||
bool isAwg, QObject *parent)
|
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||||
|
QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||||
{
|
{
|
||||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
m_serverConfigPath =
|
||||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
m_serverPublicKeyPath =
|
||||||
|
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||||
|
m_serverPskKeyPath =
|
||||||
|
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||||
|
|
||||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||||
@@ -63,9 +68,31 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
|
||||||
|
{
|
||||||
|
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
|
||||||
|
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
|
||||||
|
|
||||||
|
QList<QHostAddress> ips;
|
||||||
|
|
||||||
|
while (matchIterator.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matchIterator.next();
|
||||||
|
const QString address_string { match.captured(1) };
|
||||||
|
const QHostAddress address { address_string };
|
||||||
|
if (address.isNull()) {
|
||||||
|
qWarning() << "Couldn't recognize the ip address: " << address_string;
|
||||||
|
} else {
|
||||||
|
ips << address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips;
|
||||||
|
}
|
||||||
|
|
||||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||||
DockerContainer container,
|
DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const QJsonObject &containerConfig,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of already created clients (only IP addresses)
|
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||||
QString nextIpNumber;
|
|
||||||
{
|
|
||||||
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
auto ips = getIpsFromConf(stdOut);
|
||||||
|
|
||||||
stdOut.replace("AllowedIPs = ", "");
|
QHostAddress nextIp = [&] {
|
||||||
stdOut.replace("/32", "");
|
QHostAddress result;
|
||||||
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
QHostAddress lastIp;
|
||||||
|
if (ips.empty()) {
|
||||||
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
lastIp.setAddress(containerConfig.value(m_protocolName)
|
||||||
// and added there more IPs for route his itnernal networks, like:
|
.toObject()
|
||||||
// ...
|
.value(config_key::subnet_address)
|
||||||
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
|
.toString(protocols::wireguard::defaultSubnetAddress));
|
||||||
// ...
|
|
||||||
// without this code - next IP would be 1 if last item in 'ips' has format above
|
|
||||||
QStringList vpnIps;
|
|
||||||
for (const auto &ip : ips) {
|
|
||||||
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
|
|
||||||
}
|
|
||||||
ips = vpnIps;
|
|
||||||
|
|
||||||
// Calc next IP address
|
|
||||||
if (ips.isEmpty()) {
|
|
||||||
nextIpNumber = "2";
|
|
||||||
} else {
|
} else {
|
||||||
int next = ips.last().split(".").last().toInt() + 1;
|
lastIp = ips.last();
|
||||||
if (next > 254) {
|
|
||||||
errorCode = ErrorCode::AddressPoolError;
|
|
||||||
return connData;
|
|
||||||
}
|
|
||||||
nextIpNumber = QString::number(next);
|
|
||||||
}
|
}
|
||||||
|
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
|
||||||
|
switch (lastOctet) {
|
||||||
|
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
|
||||||
|
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
||||||
|
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
return result;
|
||||||
{
|
}();
|
||||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
|
||||||
if (l.isEmpty()) {
|
|
||||||
errorCode = ErrorCode::AddressPoolError;
|
|
||||||
return connData;
|
|
||||||
}
|
|
||||||
l.removeLast();
|
|
||||||
l.append(nextIpNumber);
|
|
||||||
|
|
||||||
connData.clientIP = l.join(".");
|
connData.clientIP = nextIp.toString();
|
||||||
}
|
|
||||||
|
|
||||||
// Get keys
|
// Get keys
|
||||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
connData.serverPubKey =
|
||||||
|
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||||
connData.serverPubKey.replace("\n", "");
|
connData.serverPubKey.replace("\n", "");
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
@@ -161,10 +168,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||||
|
.arg(m_serverConfigPath);
|
||||||
|
|
||||||
errorCode = m_serverController->runScript(
|
errorCode = m_serverController->runScript(
|
||||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
credentials,
|
||||||
|
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||||
|
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
@@ -173,8 +182,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
|||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||||
QString config =
|
QString config = m_serverController->replaceVars(
|
||||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
|||||||
return QJsonDocument(jConfig).toJson();
|
return QJsonDocument(jConfig).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
||||||
QString &protocolConfigString)
|
const bool isApiConfig, QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
return protocolConfigString;
|
return protocolConfigString;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
||||||
QString &protocolConfigString)
|
const bool isApiConfig, QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||||
#define WIREGUARD_CONFIGURATOR_H
|
#define WIREGUARD_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||||
QObject *parent = nullptr);
|
bool isAwg, QObject *parent = nullptr);
|
||||||
|
|
||||||
struct ConnectionData
|
struct ConnectionData
|
||||||
{
|
{
|
||||||
@@ -26,15 +27,18 @@ public:
|
|||||||
QString port;
|
QString port;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
ErrorCode &errorCode);
|
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
QString &protocolConfigString);
|
||||||
|
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||||
|
QString &protocolConfigString);
|
||||||
|
|
||||||
static ConnectionData genClientKeys();
|
static ConnectionData genClientKeys();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QList<QHostAddress> getIpsFromConf(const QString &input);
|
||||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||||
|
|
||||||
|
|||||||
@@ -140,98 +140,83 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{ DockerContainer::OpenVpn,
|
{ DockerContainer::OpenVpn,
|
||||||
QObject::tr(
|
QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
|
||||||
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
"It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
|
||||||
"It employs its unique security protocol, "
|
"and is continuously improved by the community due to its open-source nature. "
|
||||||
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
"It provides a good balance between speed and security but is easily recognized by DPI systems, "
|
||||||
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
"making it susceptible to blocking.\n"
|
||||||
"catering to a wide range of devices and operating systems. "
|
"\nFeatures:\n"
|
||||||
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"which continually reinforces its security. "
|
"* Normal battery consumption on mobile devices\n"
|
||||||
"With a strong balance of performance, security, and compatibility, "
|
"* Flexible customization for various devices and OS\n"
|
||||||
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
"* Operates over both TCP and UDP protocols") },
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
|
||||||
"* Normal power consumption on mobile devices\n"
|
|
||||||
"* Flexible customisation to suit user needs to work with different operating systems and devices\n"
|
|
||||||
"* Recognised by DPI systems and therefore susceptible to blocking\n"
|
|
||||||
"* Can operate over both TCP and UDP network protocols.") },
|
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
|
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
|
||||||
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
|
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
|
||||||
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
|
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
|
||||||
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
|
"\nFeatures:\n"
|
||||||
"* Available in the AmneziaVPN only on desktop platforms\n"
|
"* Available in AmneziaVPN only on desktop platforms\n"
|
||||||
"* Configurable encryption protocol\n"
|
"* Customizable encryption protocol\n"
|
||||||
"* Detectable by some DPI systems\n"
|
"* Detectable by some DPI systems\n"
|
||||||
"* Works over TCP network protocol.") },
|
"* Operates over TCP protocol\n") },
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
|
||||||
"protecting against detection.\n\n"
|
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
|
||||||
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
"\nThe Cloak plugin further protects the connection from DPI detection. "
|
||||||
"and the server.\n\n"
|
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
|
||||||
"Cloak protects OpenVPN from detection. \n\n"
|
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
|
||||||
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
|
||||||
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
"\nFeatures:\n"
|
||||||
"being detected\n\n"
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
|
|
||||||
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
|
|
||||||
"invisible to analysis systems.\n\n"
|
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
|
||||||
"* High power consumption on mobile devices\n"
|
"* High power consumption on mobile devices\n"
|
||||||
"* Flexible settings\n"
|
"* Flexible configuration options\n"
|
||||||
"* Not recognised by detection systems\n"
|
"* Undetectable by DPI systems\n"
|
||||||
"* Works over TCP network protocol, 443 port.\n") },
|
"* Operates over TCP protocol on port 443") },
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
|
||||||
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
|
||||||
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
"However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
|
||||||
"WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. "
|
"\nFeatures:\n"
|
||||||
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
"* Low power consumption on mobile devices\n"
|
||||||
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
"* Minimal configuration required\n"
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Easily detected by DPI systems (susceptible to blocking)\n"
|
||||||
"* Low power consumption\n"
|
"* Operates over UDP protocol") },
|
||||||
"* Minimum number of settings\n"
|
|
||||||
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
|
|
||||||
"* Works over UDP network protocol.") },
|
|
||||||
{ DockerContainer::Awg,
|
{ DockerContainer::Awg,
|
||||||
QObject::tr("A modern iteration of the popular VPN protocol, "
|
QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
|
||||||
"AmneziaWG builds upon the foundation set by WireGuard, "
|
"combining simplified architecture with high performance across all devices. "
|
||||||
"retaining its simplified architecture and high-performance capabilities across devices.\n"
|
"It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
|
||||||
"While WireGuard is known for its efficiency, "
|
"making VPN traffic indistinguishable from regular internet traffic.\n"
|
||||||
"it had issues with being easily detected due to its distinct packet signatures. "
|
"\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
|
||||||
"AmneziaWG solves this problem by using better obfuscation methods, "
|
"\nFeatures:\n"
|
||||||
"making its traffic blend in with regular internet traffic.\n"
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"This means that AmneziaWG keeps the fast performance of the original "
|
"* Low battery consumption on mobile devices\n"
|
||||||
"while adding an extra layer of stealth, "
|
"* Minimal settings required\n"
|
||||||
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n"
|
"* Undetectable by traffic analysis systems (DPI)\n"
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Operates over UDP protocol") },
|
||||||
"* Low power consumption\n"
|
|
||||||
"* Minimum number of settings\n"
|
|
||||||
"* Not recognised by traffic analysis systems\n"
|
|
||||||
"* Works over UDP network protocol.") },
|
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
|
||||||
"is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n"
|
"REALITY identifies censorship systems during the TLS handshake, "
|
||||||
"It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, "
|
"redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
|
||||||
"thus presenting an authentic TLS certificate and data. \n"
|
"This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
|
||||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
"\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
|
||||||
"legitimate sites without the need for specific configurations. \n"
|
"effectively protecting against DPI and other traffic analysis methods.\n"
|
||||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
"\nFeatures:\n"
|
||||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. "
|
"* Resistant to active probing and DPI detection\n"
|
||||||
"This makes REALITY a robust solution for maintaining internet freedom.")
|
"* No special configuration required to disguise traffic\n"
|
||||||
},
|
"* Highly effective in heavily censored regions\n"
|
||||||
|
"* Minimal battery consumption on devices\n"
|
||||||
|
"* Operates over TCP protocol") },
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
|
||||||
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
"It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
|
||||||
"making it particularly adaptive in dynamic network environments. \n"
|
"While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
|
||||||
"While it offers a blend of security, stability, and speed, "
|
"\nFeatures:\n"
|
||||||
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n"
|
"* Available in AmneziaVPN only on Windows\n"
|
||||||
"* Available in the AmneziaVPN only on Windows\n"
|
"* Low battery consumption on mobile devices\n"
|
||||||
"* Low power consumption, on mobile devices\n"
|
"* Minimal configuration required\n"
|
||||||
"* Minimal configuration\n"
|
"* Detectable by DPI analysis systems(easily blocked)\n"
|
||||||
"* Recognised by DPI analysis systems\n"
|
"* Operates over UDP protocol(ports 500 and 4500)") },
|
||||||
"* Works over UDP network protocol, ports 500 and 4500.") },
|
|
||||||
|
|
||||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||||
@@ -276,6 +261,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
#elif defined(Q_OS_IOS)
|
#elif defined(Q_OS_IOS)
|
||||||
|
// Standard iOS build (without Network Extension limitations)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
@@ -284,7 +270,23 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
case DockerContainer::Cloak: return true;
|
case DockerContainer::Cloak: return true;
|
||||||
case DockerContainer::SSXray: return true;
|
case DockerContainer::SSXray: return true;
|
||||||
// case DockerContainer::ShadowSocks: return true;
|
// case DockerContainer::ShadowSocks: return true;
|
||||||
default: return false;
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(MACOS_NE)
|
||||||
|
// macOS build using Network Extension – hide OpenVPN-based containers
|
||||||
|
switch (c) {
|
||||||
|
case DockerContainer::WireGuard: return true;
|
||||||
|
case DockerContainer::Awg: return true;
|
||||||
|
case DockerContainer::Xray: return true;
|
||||||
|
case DockerContainer::SSXray: return true;
|
||||||
|
case DockerContainer::OpenVpn:
|
||||||
|
case DockerContainer::Cloak:
|
||||||
|
case DockerContainer::ShadowSocks:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#elif defined(Q_OS_MAC)
|
#elif defined(Q_OS_MAC)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ namespace apiDefs
|
|||||||
AmneziaFreeV3,
|
AmneziaFreeV3,
|
||||||
AmneziaPremiumV1,
|
AmneziaPremiumV1,
|
||||||
AmneziaPremiumV2,
|
AmneziaPremiumV2,
|
||||||
SelfHosted
|
SelfHosted,
|
||||||
|
ExternalPremium
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ConfigSource {
|
enum ConfigSource {
|
||||||
@@ -21,12 +22,21 @@ namespace apiDefs
|
|||||||
namespace key
|
namespace key
|
||||||
{
|
{
|
||||||
constexpr QLatin1String configVersion("config_version");
|
constexpr QLatin1String configVersion("config_version");
|
||||||
|
constexpr QLatin1String apiEndpoint("api_endpoint");
|
||||||
|
constexpr QLatin1String apiKey("api_key");
|
||||||
|
constexpr QLatin1String description("description");
|
||||||
|
constexpr QLatin1String name("name");
|
||||||
|
constexpr QLatin1String protocol("protocol");
|
||||||
|
|
||||||
constexpr QLatin1String apiConfig("api_config");
|
constexpr QLatin1String apiConfig("api_config");
|
||||||
constexpr QLatin1String stackType("stack_type");
|
constexpr QLatin1String stackType("stack_type");
|
||||||
constexpr QLatin1String serviceType("service_type");
|
constexpr QLatin1String serviceType("service_type");
|
||||||
|
constexpr QLatin1String cliVersion("cli_version");
|
||||||
|
constexpr QLatin1String supportedProtocols("supported_protocols");
|
||||||
|
|
||||||
constexpr QLatin1String vpnKey("vpn_key");
|
constexpr QLatin1String vpnKey("vpn_key");
|
||||||
|
constexpr QLatin1String config("config");
|
||||||
|
constexpr QLatin1String configs("configs");
|
||||||
|
|
||||||
constexpr QLatin1String installationUuid("installation_uuid");
|
constexpr QLatin1String installationUuid("installation_uuid");
|
||||||
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
||||||
@@ -43,6 +53,21 @@ namespace apiDefs
|
|||||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
constexpr QLatin1String maxDeviceCount("max_device_count");
|
||||||
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
||||||
constexpr QLatin1String issuedConfigs("issued_configs");
|
constexpr QLatin1String issuedConfigs("issued_configs");
|
||||||
|
|
||||||
|
constexpr QLatin1String supportInfo("support_info");
|
||||||
|
constexpr QLatin1String email("email");
|
||||||
|
constexpr QLatin1String billingEmail("billing_email");
|
||||||
|
constexpr QLatin1String website("website");
|
||||||
|
constexpr QLatin1String websiteName("website_name");
|
||||||
|
constexpr QLatin1String telegram("telegram");
|
||||||
|
|
||||||
|
constexpr QLatin1String id("id");
|
||||||
|
constexpr QLatin1String orderId("order_id");
|
||||||
|
constexpr QLatin1String migrationCode("migration_code");
|
||||||
|
|
||||||
|
constexpr QLatin1String transactionId("transaction_id");
|
||||||
|
|
||||||
|
constexpr QLatin1String userCountryCode("user_country_code");
|
||||||
}
|
}
|
||||||
|
|
||||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
|
|||||||
@@ -3,9 +3,27 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
|
||||||
|
|
||||||
|
QString escapeUnicode(const QString &input)
|
||||||
|
{
|
||||||
|
QString output;
|
||||||
|
for (QChar c : input) {
|
||||||
|
if (c.unicode() < 0x20 || c.unicode() > 0x7E) {
|
||||||
|
output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0'));
|
||||||
|
} else {
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||||
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
|
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
|
||||||
return endDate < now;
|
return endDate < now;
|
||||||
}
|
}
|
||||||
@@ -23,24 +41,34 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
|
|||||||
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
||||||
{
|
{
|
||||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
||||||
|
|
||||||
switch (configVersion) {
|
switch (configVersion) {
|
||||||
case apiDefs::ConfigSource::Telegram: {
|
case apiDefs::ConfigSource::Telegram: {
|
||||||
|
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||||
|
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||||
|
|
||||||
|
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
|
||||||
|
|
||||||
|
if (apiEndpoint.contains(premiumV1Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaPremiumV1;
|
||||||
|
} else if (apiEndpoint.contains(freeV2Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaFreeV2;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
case apiDefs::ConfigSource::AmneziaGateway: {
|
case apiDefs::ConfigSource::AmneziaGateway: {
|
||||||
constexpr QLatin1String stackPremium("prem");
|
|
||||||
constexpr QLatin1String stackFree("free");
|
|
||||||
|
|
||||||
constexpr QLatin1String servicePremium("amnezia-premium");
|
constexpr QLatin1String servicePremium("amnezia-premium");
|
||||||
constexpr QLatin1String serviceFree("amnezia-free");
|
constexpr QLatin1String serviceFree("amnezia-free");
|
||||||
|
constexpr QLatin1String serviceExternalPremium("external-premium");
|
||||||
|
|
||||||
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
||||||
auto stackType = apiConfigObject.value(apiDefs::key::stackType).toString();
|
|
||||||
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
|
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
|
||||||
|
|
||||||
if (serviceType == servicePremium || stackType == stackPremium) {
|
if (serviceType == servicePremium) {
|
||||||
return apiDefs::ConfigType::AmneziaPremiumV2;
|
return apiDefs::ConfigType::AmneziaPremiumV2;
|
||||||
} else if (serviceType == serviceFree || stackType == stackFree) {
|
} else if (serviceType == serviceFree) {
|
||||||
return apiDefs::ConfigType::AmneziaFreeV3;
|
return apiDefs::ConfigType::AmneziaFreeV3;
|
||||||
|
} else if (serviceType == serviceExternalPremium) {
|
||||||
|
return apiDefs::ConfigType::ExternalPremium;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -54,7 +82,9 @@ apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigO
|
|||||||
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
|
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
||||||
|
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
||||||
|
const QByteArray &responseBody)
|
||||||
{
|
{
|
||||||
const int httpStatusCodeConflict = 409;
|
const int httpStatusCodeConflict = 409;
|
||||||
const int httpStatusCodeNotFound = 404;
|
const int httpStatusCodeNotFound = 404;
|
||||||
@@ -62,17 +92,19 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
if (!sslErrors.empty()) {
|
if (!sslErrors.empty()) {
|
||||||
qDebug().noquote() << sslErrors;
|
qDebug().noquote() << sslErrors;
|
||||||
return amnezia::ErrorCode::ApiConfigSslError;
|
return amnezia::ErrorCode::ApiConfigSslError;
|
||||||
} else if (reply->error() == QNetworkReply::NoError) {
|
} else if (replyError == QNetworkReply::NoError) {
|
||||||
return amnezia::ErrorCode::NoError;
|
return amnezia::ErrorCode::NoError;
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
} else if (replyError == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|| replyError == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
qDebug() << replyError;
|
||||||
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
||||||
|
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||||
|
qDebug() << replyError;
|
||||||
|
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||||
} else {
|
} else {
|
||||||
QString err = reply->errorString();
|
qDebug() << QString::fromUtf8(responseBody);
|
||||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
qDebug() << replyError;
|
||||||
qDebug() << QString::fromUtf8(reply->readAll());
|
qDebug() << replyErrorString;
|
||||||
qDebug() << reply->error();
|
|
||||||
qDebug() << err;
|
|
||||||
qDebug() << httpStatusCode;
|
qDebug() << httpStatusCode;
|
||||||
if (httpStatusCode == httpStatusCodeConflict) {
|
if (httpStatusCode == httpStatusCodeConflict) {
|
||||||
return amnezia::ErrorCode::ApiConfigLimitError;
|
return amnezia::ErrorCode::ApiConfigLimitError;
|
||||||
@@ -85,3 +117,96 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
qDebug() << "something went wrong";
|
qDebug() << "something went wrong";
|
||||||
return amnezia::ErrorCode::InternalError;
|
return amnezia::ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
|
||||||
|
apiDefs::ConfigType::ExternalPremium };
|
||||||
|
return premiumTypes.contains(getConfigType(serverConfigObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPair<QString, QVariant>> orderedFields;
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString()));
|
||||||
|
|
||||||
|
QString vpnKeyStr = "{";
|
||||||
|
for (int i = 0; i < orderedFields.size(); ++i) {
|
||||||
|
const auto &pair = orderedFields[i];
|
||||||
|
if (pair.second.typeId() == QMetaType::Type::QString) {
|
||||||
|
vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\"";
|
||||||
|
} else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) {
|
||||||
|
vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < orderedFields.size() - 1) {
|
||||||
|
vpnKeyStr += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vpnKeyStr += "}";
|
||||||
|
|
||||||
|
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
|
||||||
|
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
|
||||||
|
vpnKeyCompressed = vpnKeyCompressed.mid(4);
|
||||||
|
|
||||||
|
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
|
||||||
|
|
||||||
|
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString vpnKeyText = "";
|
||||||
|
|
||||||
|
auto apiConfig = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
||||||
|
auto authData = serverConfigObject.value(QLatin1String("auth_data")).toObject();
|
||||||
|
|
||||||
|
const QString name = serverConfigObject.value(apiDefs::key::name).toString();
|
||||||
|
const QString description = serverConfigObject.value(apiDefs::key::description).toString();
|
||||||
|
const double configVersion = serverConfigObject.value(apiDefs::key::configVersion).toDouble();
|
||||||
|
|
||||||
|
const QString serviceType = apiConfig.value(apiDefs::key::serviceType).toString();
|
||||||
|
const QString serviceProtocol = apiConfig.value(QLatin1String("service_protocol")).toString();
|
||||||
|
const QString userCountryCode = apiConfig.value(QLatin1String("user_country_code")).toString();
|
||||||
|
|
||||||
|
const QString apiKey = authData.value(apiDefs::key::apiKey).toString();
|
||||||
|
|
||||||
|
QString vpnKeyStr = "{";
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::name) + "\": \"" + name + "\", ";
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::description) + "\": \"" + description + "\", ";
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::configVersion) + "\": " + QString::number(static_cast<int>(configVersion)) + ", ";
|
||||||
|
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::apiConfig) + "\": {";
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::serviceType) + "\": \"" + serviceType + "\", ";
|
||||||
|
vpnKeyStr += "\"service_protocol\": \"" + serviceProtocol + "\", ";
|
||||||
|
vpnKeyStr += "\"user_country_code\": \"" + userCountryCode + "\"";
|
||||||
|
vpnKeyStr += "}, ";
|
||||||
|
|
||||||
|
vpnKeyStr += "\"auth_data\": {";
|
||||||
|
vpnKeyStr += "\"" + QString(apiDefs::key::apiKey) + "\": \"" + apiKey + "\"";
|
||||||
|
vpnKeyStr += "}";
|
||||||
|
|
||||||
|
vpnKeyStr += "}";
|
||||||
|
|
||||||
|
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
|
||||||
|
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
|
||||||
|
vpnKeyCompressed = vpnKeyCompressed.mid(4);
|
||||||
|
|
||||||
|
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
|
||||||
|
vpnKeyText = QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
|
||||||
|
|
||||||
|
return vpnKeyText;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,10 +13,17 @@ namespace apiUtils
|
|||||||
|
|
||||||
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
||||||
|
|
||||||
|
bool isPremiumServer(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
|
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
|
||||||
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
||||||
|
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
||||||
|
const QByteArray &responseBody);
|
||||||
|
|
||||||
|
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
|
||||||
|
QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // APIUTILS_H
|
#endif // APIUTILS_H
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "coreController.h"
|
#include "coreController.h"
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
@@ -25,9 +26,8 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
|
|||||||
|
|
||||||
initNotificationHandler();
|
initNotificationHandler();
|
||||||
|
|
||||||
auto locale = m_settings->getAppLanguage();
|
|
||||||
m_translator.reset(new QTranslator());
|
m_translator.reset(new QTranslator());
|
||||||
updateTranslator(locale);
|
updateTranslator(m_settings->getAppLanguage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initModels()
|
void CoreController::initModels()
|
||||||
@@ -47,6 +47,9 @@ void CoreController::initModels()
|
|||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||||
|
|
||||||
|
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
||||||
|
|
||||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||||
|
|
||||||
@@ -96,6 +99,9 @@ void CoreController::initModels()
|
|||||||
|
|
||||||
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
||||||
|
|
||||||
|
m_newsModel.reset(new NewsModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initControllers()
|
void CoreController::initControllers()
|
||||||
@@ -116,6 +122,9 @@ void CoreController::initControllers()
|
|||||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
||||||
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
||||||
|
|
||||||
|
connect(m_installController.get(), &InstallController::profileCleared,
|
||||||
|
m_protocolsModel.get(), &ProtocolsModel::updateModel);
|
||||||
|
|
||||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||||
|
|
||||||
@@ -129,6 +138,9 @@ void CoreController::initControllers()
|
|||||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||||
|
|
||||||
|
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
||||||
|
|
||||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||||
|
|
||||||
@@ -141,6 +153,12 @@ void CoreController::initControllers()
|
|||||||
|
|
||||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
||||||
|
|
||||||
|
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
||||||
|
|
||||||
|
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
void CoreController::initAndroidController()
|
||||||
@@ -213,11 +231,14 @@ void CoreController::initSignalHandlers()
|
|||||||
initAutoConnectHandler();
|
initAutoConnectHandler();
|
||||||
initAmneziaDnsToggledHandler();
|
initAmneziaDnsToggledHandler();
|
||||||
initPrepareConfigHandler();
|
initPrepareConfigHandler();
|
||||||
|
initImportPremiumV2VpnKeyHandler();
|
||||||
|
initShowMigrationDrawerHandler();
|
||||||
|
initStrictKillSwitchHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
void CoreController::initNotificationHandler()
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_ANDROID
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||||
|
|
||||||
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
||||||
@@ -229,6 +250,9 @@ void CoreController::initNotificationHandler()
|
|||||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||||
&ConnectionController::closeConnection);
|
&ConnectionController::closeConnection);
|
||||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||||
|
|
||||||
|
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
|
||||||
|
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +262,23 @@ void CoreController::updateTranslator(const QLocale &locale)
|
|||||||
QCoreApplication::removeTranslator(m_translator.get());
|
QCoreApplication::removeTranslator(m_translator.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
|
QStringList availableTranslations;
|
||||||
|
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
availableTranslations << it.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code allow to load translation for the language only, without country code
|
||||||
|
const QString lang = locale.name().split("_").first();
|
||||||
|
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
|
||||||
|
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
|
||||||
|
for (const QString &translation : availableTranslations) {
|
||||||
|
if (translation.contains(translationFilePrefix)) {
|
||||||
|
strFileName = translation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_translator->load(strFileName)) {
|
if (m_translator->load(strFileName)) {
|
||||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||||
m_settings->setAppLanguage(locale);
|
m_settings->setAppLanguage(locale);
|
||||||
@@ -250,6 +290,7 @@ void CoreController::updateTranslator(const QLocale &locale)
|
|||||||
m_engine->retranslate();
|
m_engine->retranslate();
|
||||||
|
|
||||||
emit translationsUpdated();
|
emit translationsUpdated();
|
||||||
|
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initErrorMessagesHandler()
|
void CoreController::initErrorMessagesHandler()
|
||||||
@@ -270,13 +311,10 @@ void CoreController::setQmlRoot()
|
|||||||
|
|
||||||
void CoreController::initApiCountryModelUpdateHandler()
|
void CoreController::initApiCountryModelUpdateHandler()
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
||||||
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
||||||
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
||||||
});
|
});
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
|
|
||||||
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initContainerModelUpdateHandler()
|
void CoreController::initContainerModelUpdateHandler()
|
||||||
@@ -284,6 +322,11 @@ void CoreController::initContainerModelUpdateHandler()
|
|||||||
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
||||||
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
||||||
&ContainersModel::updateModel);
|
&ContainersModel::updateModel);
|
||||||
|
connect(m_serversModel.get(), &ServersModel::gatewayStacksExpanded, this, [this]() {
|
||||||
|
if (m_serversModel->hasServersFromGatewayApi()) {
|
||||||
|
m_apiNewsController->fetchNews();
|
||||||
|
}
|
||||||
|
});
|
||||||
m_serversModel->resetModel();
|
m_serversModel->resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +382,31 @@ void CoreController::initPrepareConfigHandler()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoreController::initImportPremiumV2VpnKeyHandler()
|
||||||
|
{
|
||||||
|
connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
|
||||||
|
m_importController->extractConfigFromData(vpnKey);
|
||||||
|
m_importController->importConfig();
|
||||||
|
|
||||||
|
emit m_apiPremV1MigrationController->migrationFinished();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::initShowMigrationDrawerHandler()
|
||||||
|
{
|
||||||
|
QTimer::singleShot(1000, this, [this]() {
|
||||||
|
if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) {
|
||||||
|
m_apiPremV1MigrationController->showMigrationDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::initStrictKillSwitchHandler()
|
||||||
|
{
|
||||||
|
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
||||||
|
&VpnConnection::onKillSwitchModeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<PageController> CoreController::pageController() const
|
QSharedPointer<PageController> CoreController::pageController() const
|
||||||
{
|
{
|
||||||
return m_pageController;
|
return m_pageController;
|
||||||
|
|||||||
@@ -5,9 +5,16 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
#include "ui/systemtray_notificationhandler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/apiConfigsController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiSettingsController.h"
|
||||||
|
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
||||||
|
#include "ui/controllers/api/apiNewsController.h"
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
#include "ui/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "ui/controllers/allowedDnsController.h"
|
||||||
#include "ui/controllers/connectionController.h"
|
#include "ui/controllers/connectionController.h"
|
||||||
#include "ui/controllers/exportController.h"
|
#include "ui/controllers/exportController.h"
|
||||||
#include "ui/controllers/focusController.h"
|
#include "ui/controllers/focusController.h"
|
||||||
@@ -18,6 +25,7 @@
|
|||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
|
||||||
|
#include "ui/models/allowed_dns_model.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
#include "ui/models/languageModel.h"
|
#include "ui/models/languageModel.h"
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
#include "ui/models/protocols/cloakConfigModel.h"
|
||||||
@@ -40,8 +48,9 @@
|
|||||||
#include "ui/models/services/sftpConfigModel.h"
|
#include "ui/models/services/sftpConfigModel.h"
|
||||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||||
#include "ui/models/sites_model.h"
|
#include "ui/models/sites_model.h"
|
||||||
|
#include "ui/models/newsModel.h"
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
#include "ui/notificationhandler.h"
|
#include "ui/notificationhandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -58,6 +67,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void translationsUpdated();
|
void translationsUpdated();
|
||||||
|
void websiteUrlChanged(const QString &newUrl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initModels();
|
void initModels();
|
||||||
@@ -80,13 +90,16 @@ private:
|
|||||||
void initAutoConnectHandler();
|
void initAutoConnectHandler();
|
||||||
void initAmneziaDnsToggledHandler();
|
void initAmneziaDnsToggledHandler();
|
||||||
void initPrepareConfigHandler();
|
void initPrepareConfigHandler();
|
||||||
|
void initImportPremiumV2VpnKeyHandler();
|
||||||
|
void initShowMigrationDrawerHandler();
|
||||||
|
void initStrictKillSwitchHandler();
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||||
QSharedPointer<QTranslator> m_translator;
|
QSharedPointer<QTranslator> m_translator;
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -102,9 +115,12 @@ private:
|
|||||||
QScopedPointer<SitesController> m_sitesController;
|
QScopedPointer<SitesController> m_sitesController;
|
||||||
QScopedPointer<SystemController> m_systemController;
|
QScopedPointer<SystemController> m_systemController;
|
||||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||||
|
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||||
|
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
|
||||||
|
QScopedPointer<ApiNewsController> m_apiNewsController;
|
||||||
|
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
||||||
@@ -112,6 +128,8 @@ private:
|
|||||||
QSharedPointer<LanguageModel> m_languageModel;
|
QSharedPointer<LanguageModel> m_languageModel;
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
QSharedPointer<SitesModel> m_sitesModel;
|
||||||
|
QSharedPointer<NewsModel> m_newsModel;
|
||||||
|
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,20 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "QBlockCipher.h"
|
#include "QBlockCipher.h"
|
||||||
#include "QRsa.h"
|
#include "QRsa.h"
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
|
#include "core/networkUtilities.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
#include "core/ipcclient.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
namespace configKey
|
namespace configKey
|
||||||
@@ -26,64 +32,24 @@ namespace
|
|||||||
constexpr char apiPayload[] = "api_payload";
|
constexpr char apiPayload[] = "api_payload";
|
||||||
constexpr char keyPayload[] = "key_payload";
|
constexpr char keyPayload[] = "key_payload";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
||||||
|
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
||||||
|
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
||||||
|
|
||||||
|
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
|
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||||
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs)
|
const bool isStrictKillSwitchEnabled, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_gatewayEndpoint(gatewayEndpoint),
|
||||||
|
m_isDevEnvironment(isDevEnvironment),
|
||||||
|
m_requestTimeoutMsecs(requestTimeoutMsecs),
|
||||||
|
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_IOS
|
|
||||||
IosController::Instance()->requestInetAccess();
|
|
||||||
QThread::msleep(10);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
|
||||||
|
|
||||||
QNetworkReply *reply;
|
|
||||||
reply = amnApp->networkManager()->get(request);
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
|
||||||
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
responseBody = reply->readAll();
|
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
|
||||||
auto requestFunction = [&request, &responseBody](const QString &url) {
|
|
||||||
request.setUrl(url);
|
|
||||||
return amnApp->networkManager()->get(request);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
|
|
||||||
const QList<QSslError> &nestedSslErrors) {
|
|
||||||
responseBody = nestedReply->readAll();
|
|
||||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
|
|
||||||
sslErrors = nestedSslErrors;
|
|
||||||
reply = nestedReply;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
@@ -94,8 +60,20 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
|
||||||
|
|
||||||
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
||||||
|
|
||||||
|
// bypass killSwitch exceptions for API-gateway
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
if (m_isStrictKillSwitchEnabled) {
|
||||||
|
QString host = QUrl(request.url()).host();
|
||||||
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
|
if (!ip.isEmpty()) {
|
||||||
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||||
@@ -147,29 +125,37 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
QByteArray encryptedResponseBody = reply->readAll();
|
||||||
|
QString replyErrorString = reply->errorString();
|
||||||
|
auto replyError = reply->error();
|
||||||
|
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
||||||
request.setUrl(url);
|
request.setUrl(url);
|
||||||
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
};
|
};
|
||||||
|
|
||||||
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &key, &iv,
|
||||||
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
&salt, this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
|
||||||
encryptedResponseBody = nestedReply->readAll();
|
encryptedResponseBody = reply->readAll();
|
||||||
reply = nestedReply;
|
replyErrorString = reply->errorString();
|
||||||
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
replyError = reply->error();
|
||||||
|
httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
if (!sslErrors.isEmpty() || shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
sslErrors = nestedSslErrors;
|
sslErrors = nestedSslErrors;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
|
||||||
|
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
|
||||||
|
bypassProxy(endpoint, serviceType, userCountryCode, requestFunction, replyProcessingFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, replyErrorString, replyError, httpStatusCode, encryptedResponseBody);
|
||||||
reply->deleteLater();
|
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -184,7 +170,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList GatewayController::getProxyUrls()
|
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
|
||||||
{
|
{
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||||
@@ -194,16 +180,27 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
QList<QSslError> sslErrors;
|
QList<QSslError> sslErrors;
|
||||||
QNetworkReply *reply;
|
QNetworkReply *reply;
|
||||||
|
|
||||||
QStringList proxyStorageUrl;
|
QStringList baseUrls;
|
||||||
if (m_isDevEnvironment) {
|
if (m_isDevEnvironment) {
|
||||||
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
|
||||||
} else {
|
} else {
|
||||||
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||||
|
|
||||||
for (const auto &proxyStorageUrl : proxyStorageUrl) {
|
QStringList proxyStorageUrls;
|
||||||
|
if (!serviceType.isEmpty()) {
|
||||||
|
for (const auto &baseUrl : baseUrls) {
|
||||||
|
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
|
||||||
|
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &baseUrl : baseUrls) {
|
||||||
|
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &proxyStorageUrl : proxyStorageUrls) {
|
||||||
request.setUrl(proxyStorageUrl);
|
request.setUrl(proxyStorageUrl);
|
||||||
reply = amnApp->networkManager()->get(request);
|
reply = amnApp->networkManager()->get(request);
|
||||||
|
|
||||||
@@ -247,56 +244,126 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
}
|
}
|
||||||
return endpoints;
|
return endpoints;
|
||||||
} else {
|
} else {
|
||||||
|
auto replyError = reply->error();
|
||||||
|
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
qDebug() << replyError;
|
||||||
|
qDebug() << httpStatusCode;
|
||||||
|
qDebug() << "go to the next storage endpoint";
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody,
|
||||||
const QByteArray &iv, const QByteArray &salt)
|
bool checkEncryption, const QByteArray &key, const QByteArray &iv, const QByteArray &salt)
|
||||||
{
|
{
|
||||||
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
if (replyError == QNetworkReply::NetworkError::OperationCanceledError || replyError == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
qDebug() << "Timeout occurred";
|
qDebug() << "timeout occurred";
|
||||||
|
qDebug() << replyError;
|
||||||
return true;
|
return true;
|
||||||
} else if (responseBody.contains("html")) {
|
} else if (responseBody.contains("html")) {
|
||||||
qDebug() << "The response contains an html tag";
|
qDebug() << "the response contains an html tag";
|
||||||
return true;
|
return true;
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
|
} else if (replyError == QNetworkReply::NetworkError::ContentNotFoundError) {
|
||||||
|
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|
||||||
|
|| responseBody.contains(errorResponsePattern3)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
qDebug() << replyError;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||||
|
if (responseBody.contains(updateRequestResponsePattern)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
qDebug() << replyError;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (replyError != QNetworkReply::NetworkError::NoError) {
|
||||||
|
qDebug() << replyError;
|
||||||
|
return true;
|
||||||
|
} else if (checkEncryption) {
|
||||||
try {
|
try {
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
qDebug() << "Failed to decrypt the data";
|
qDebug() << "failed to decrypt the data";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
|
void GatewayController::bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
|
||||||
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
||||||
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
|
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
|
||||||
{
|
{
|
||||||
QStringList proxyUrls = getProxyUrls();
|
QStringList proxyUrls = getProxyUrls(serviceType, userCountryCode);
|
||||||
std::random_device randomDevice;
|
std::random_device randomDevice;
|
||||||
std::mt19937 generator(randomDevice());
|
std::mt19937 generator(randomDevice());
|
||||||
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
|
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
|
|
||||||
for (const QString &proxyUrl : proxyUrls) {
|
auto bypassFunction = [this](const QString &endpoint, const QString &proxyUrl,
|
||||||
qDebug() << "Go to the next endpoint";
|
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
||||||
reply->deleteLater(); // delete the previous reply
|
std::function<bool(QNetworkReply * reply, const QList<QSslError> &sslErrors)> replyProcessingFunction) {
|
||||||
reply = requestFunction(endpoint.arg(proxyUrl));
|
QEventLoop wait;
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
|
||||||
|
qDebug() << "go to the next proxy endpoint";
|
||||||
|
QNetworkReply *reply = requestFunction(endpoint.arg(proxyUrl));
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (replyProcessingFunction(reply, sslErrors)) {
|
auto result = replyProcessingFunction(reply, sslErrors);
|
||||||
|
reply->deleteLater();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_proxyUrl.isEmpty()) {
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(1000);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
QNetworkReply *reply;
|
||||||
|
|
||||||
|
for (const QString &proxyUrl : proxyUrls) {
|
||||||
|
request.setUrl(proxyUrl + "lmbd-health");
|
||||||
|
reply = amnApp->networkManager()->get(request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
m_proxyUrl = proxyUrl;
|
||||||
|
if (!m_proxyUrl.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_proxyUrl.isEmpty()) {
|
||||||
|
if (bypassFunction(endpoint, m_proxyUrl, requestFunction, replyProcessingFunction)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &proxyUrl : proxyUrls) {
|
||||||
|
if (bypassFunction(endpoint, proxyUrl, requestFunction, replyProcessingFunction)) {
|
||||||
|
m_proxyUrl = proxyUrl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,21 +15,25 @@ class GatewayController : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr);
|
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||||
|
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
||||||
|
|
||||||
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
|
|
||||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList getProxyUrls();
|
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
|
||||||
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
|
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
|
||||||
const QByteArray &iv = "", const QByteArray &salt = "");
|
const QByteArray &key = "", const QByteArray &iv = "", const QByteArray &salt = "");
|
||||||
void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function<QNetworkReply *(const QString &url)> requestFunction,
|
void bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
|
||||||
|
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
||||||
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
|
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
|
||||||
|
|
||||||
int m_requestTimeoutMsecs;
|
int m_requestTimeoutMsecs;
|
||||||
QString m_gatewayEndpoint;
|
QString m_gatewayEndpoint;
|
||||||
bool m_isDevEnvironment = false;
|
bool m_isDevEnvironment = false;
|
||||||
|
bool m_isStrictKillSwitchEnabled = false;
|
||||||
|
|
||||||
|
inline static QString m_proxyUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GATEWAYCONTROLLER_H
|
#endif // GATEWAYCONTROLLER_H
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||||||
|
|
||||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||||||
return e;
|
return e;
|
||||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||||||
return e;
|
return e;
|
||||||
|
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
|||||||
|
|
||||||
errorCode = ErrorCode::NoError;
|
errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
@@ -366,8 +366,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||||||
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
||||||
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
||||||
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
||||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
||||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)))
|
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
||||||
|
// || (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
|
||||||
|
// != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
|
||||||
|
// || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
|
||||||
|
// != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize))
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +388,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (container == DockerContainer::Xray) {
|
||||||
|
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
|
||||||
|
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,15 +451,24 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
|||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
errorCode =
|
ErrorCode error =
|
||||||
runScript(credentials,
|
runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
||||||
cbReadStdOut);
|
cbReadStdOut, cbReadStdErr);
|
||||||
if (errorCode)
|
|
||||||
return errorCode;
|
|
||||||
|
|
||||||
return errorCode;
|
if (stdOut.contains("doesn't work on cgroups v2"))
|
||||||
|
return ErrorCode::ServerDockerOnCgroupsV2;
|
||||||
|
if (stdOut.contains("cgroup mountpoint does not exist"))
|
||||||
|
return ErrorCode::ServerCgroupMountpoint;
|
||||||
|
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
|
||||||
|
return ErrorCode::DockerPullRateLimit;
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||||
@@ -625,6 +646,9 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||||||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||||
|
|
||||||
|
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
|
||||||
|
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
|
||||||
|
|
||||||
// Socks5 proxy vars
|
// Socks5 proxy vars
|
||||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||||
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
||||||
@@ -709,7 +733,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
|||||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||||
|
|
||||||
// TODO reimplement with netstat
|
// TODO reimplement with netstat
|
||||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||||
for (auto &port : fixedPorts) {
|
for (auto &port : fixedPorts) {
|
||||||
script = script.append("|:%1").arg(port);
|
script = script.append("|:%1").arg(port);
|
||||||
}
|
}
|
||||||
@@ -757,10 +781,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
|||||||
|
|
||||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
||||||
{
|
{
|
||||||
if (credentials.userName == "root") {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
@@ -774,8 +794,16 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
|||||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (!stdOut.contains("sudo"))
|
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||||
|
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
||||||
|
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||||
return ErrorCode::ServerUserNotInSudo;
|
return ErrorCode::ServerUserNotInSudo;
|
||||||
|
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
|
||||||
|
return ErrorCode::ServerUserDirectoryNotAccessible;
|
||||||
|
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
||||||
|
return ErrorCode::ServerUserNotAllowedInSudoers;
|
||||||
|
if (stdOut.contains("password is required"))
|
||||||
|
return ErrorCode::ServerUserPasswordRequired;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -807,7 +835,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
|||||||
|
|
||||||
if (stdOut.contains("Packet manager not found"))
|
if (stdOut.contains("Packet manager not found"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("fuser not installed"))
|
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
|
||||||
if (stdOut.isEmpty()) {
|
if (stdOut.isEmpty()) {
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ namespace amnezia
|
|||||||
ServerCancelInstallation = 204,
|
ServerCancelInstallation = 204,
|
||||||
ServerUserNotInSudo = 205,
|
ServerUserNotInSudo = 205,
|
||||||
ServerPacketManagerError = 206,
|
ServerPacketManagerError = 206,
|
||||||
|
ServerSudoPackageIsNotPreinstalled = 207,
|
||||||
|
ServerUserDirectoryNotAccessible = 208,
|
||||||
|
ServerUserNotAllowedInSudoers = 209,
|
||||||
|
ServerUserPasswordRequired = 210,
|
||||||
|
ServerDockerOnCgroupsV2 = 211,
|
||||||
|
ServerCgroupMountpoint = 212,
|
||||||
|
DockerPullRateLimit = 213,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
@@ -111,6 +118,9 @@ namespace amnezia
|
|||||||
ApiServicesMissingError = 1107,
|
ApiServicesMissingError = 1107,
|
||||||
ApiConfigLimitError = 1108,
|
ApiConfigLimitError = 1108,
|
||||||
ApiNotFoundError = 1109,
|
ApiNotFoundError = 1109,
|
||||||
|
ApiMigrationError = 1110,
|
||||||
|
ApiUpdateRequestError = 1111,
|
||||||
|
ApiSubscriptionExpiredError = 1112,
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
OpenError = 1200,
|
OpenError = 1200,
|
||||||
|
|||||||
@@ -20,8 +20,15 @@ QString errorString(ErrorCode code) {
|
|||||||
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
||||||
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
||||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
||||||
|
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); break;
|
||||||
|
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
||||||
|
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
||||||
|
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||||
|
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
||||||
|
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
||||||
|
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
||||||
|
|
||||||
// Libssh errors
|
// Libssh errors
|
||||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||||
@@ -68,6 +75,9 @@ QString errorString(ErrorCode code) {
|
|||||||
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
||||||
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
||||||
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
||||||
|
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
|
||||||
|
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
|
||||||
|
case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break;
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include "qendian.h"
|
#include "qendian.h"
|
||||||
|
#include <QSettings>
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@@ -22,7 +23,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>
|
||||||
@@ -185,6 +186,17 @@ int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetworkUtilities::checkIpv6Enabled() {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QSettings RegHLM("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters",
|
||||||
|
QSettings::NativeFormat);
|
||||||
|
int ret = RegHLM.value("DisabledComponents", 0).toInt();
|
||||||
|
qDebug() << "Check for Windows disabled IPv6 return " << ret;
|
||||||
|
return (ret != 255);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
||||||
const ULONG Flags,
|
const ULONG Flags,
|
||||||
@@ -378,7 +390,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};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public:
|
|||||||
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
||||||
static bool checkIPv4Format(const QString &ip);
|
static bool checkIPv4Format(const QString &ip);
|
||||||
static bool checkIpSubnetFormat(const QString &ip);
|
static bool checkIpSubnetFormat(const QString &ip);
|
||||||
|
static bool checkIpv6Enabled();
|
||||||
static QString getGatewayAndIface();
|
static QString getGatewayAndIface();
|
||||||
// Returns the Interface Index that could Route to dst
|
// Returns the Interface Index that could Route to dst
|
||||||
static int AdapterIndexTo(const QHostAddress& dst);
|
static int AdapterIndexTo(const QHostAddress& dst);
|
||||||
@@ -29,7 +30,6 @@ public:
|
|||||||
|
|
||||||
static QString netMaskFromIpWithSubnet(const QString ip);
|
static QString netMaskFromIpWithSubnet(const QString ip);
|
||||||
static QString ipAddressFromIpWithSubnet(const QString ip);
|
static QString ipAddressFromIpWithSubnet(const QString ip);
|
||||||
|
|
||||||
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
|||||||
// 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)) {
|
||||||
logger.debug() << "Routing configuration failed for"
|
logger.debug() << "Routing configuration failed for" << ip.toString();
|
||||||
<< logger.sensitive(ip.toString());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,11 +169,14 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
|||||||
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||||
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
||||||
QList<QHostAddress> resolvers;
|
QList<QHostAddress> resolvers;
|
||||||
resolvers.append(QHostAddress(config.m_dnsServer));
|
resolvers.append(QHostAddress(config.m_primaryDnsServer));
|
||||||
|
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||||
|
resolvers.append(QHostAddress(config.m_secondaryDnsServer));
|
||||||
|
}
|
||||||
|
|
||||||
// If the DNS is not the Gateway, it's a user defined DNS
|
// If the DNS is not the Gateway, it's a user defined DNS
|
||||||
// thus, not add any other :)
|
// thus, not add any other :)
|
||||||
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
||||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,15 +282,26 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||||||
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
||||||
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
||||||
|
|
||||||
if (!obj.contains("dnsServer")) {
|
if (!obj.contains("primaryDnsServer")) {
|
||||||
config.m_dnsServer = QString();
|
config.m_primaryDnsServer = QString();
|
||||||
} else {
|
} else {
|
||||||
QJsonValue value = obj.value("dnsServer");
|
QJsonValue value = obj.value("primaryDnsServer");
|
||||||
if (!value.isString()) {
|
if (!value.isString()) {
|
||||||
logger.error() << "dnsServer is not a string";
|
logger.error() << "dnsServer is not a string";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
config.m_dnsServer = value.toString();
|
config.m_primaryDnsServer = value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj.contains("secondaryDnsServer")) {
|
||||||
|
config.m_secondaryDnsServer = QString();
|
||||||
|
} else {
|
||||||
|
QJsonValue value = obj.value("secondaryDnsServer");
|
||||||
|
if (!value.isString()) {
|
||||||
|
logger.error() << "dnsServer is not a string";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
config.m_secondaryDnsServer = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!obj.contains("hopType")) {
|
if (!obj.contains("hopType")) {
|
||||||
@@ -371,6 +384,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||||
|
|
||||||
@@ -389,6 +405,13 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||||||
if (!obj.value("S2").isNull()) {
|
if (!obj.value("S2").isNull()) {
|
||||||
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
||||||
}
|
}
|
||||||
|
if (!obj.value("S3").isNull()) {
|
||||||
|
config.m_cookieReplyPacketJunkSize = obj.value("S3").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("S4").isNull()) {
|
||||||
|
config.m_transportPacketJunkSize = obj.value("S4").toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (!obj.value("H1").isNull()) {
|
if (!obj.value("H1").isNull()) {
|
||||||
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
||||||
}
|
}
|
||||||
@@ -402,6 +425,34 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
|||||||
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!obj.value("I1").isNull()) {
|
||||||
|
config.m_specialJunk["I1"] = obj.value("I1").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("I2").isNull()) {
|
||||||
|
config.m_specialJunk["I2"] = obj.value("I2").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("I3").isNull()) {
|
||||||
|
config.m_specialJunk["I3"] = obj.value("I3").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("I4").isNull()) {
|
||||||
|
config.m_specialJunk["I4"] = obj.value("I4").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("I5").isNull()) {
|
||||||
|
config.m_specialJunk["I5"] = obj.value("I5").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("J1").isNull()) {
|
||||||
|
config.m_controlledJunk["J1"] = obj.value("J1").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("J2").isNull()) {
|
||||||
|
config.m_controlledJunk["J2"] = obj.value("J2").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("J3").isNull()) {
|
||||||
|
config.m_controlledJunk["J3"] = obj.value("J3").toString();
|
||||||
|
}
|
||||||
|
if (!obj.value("Itime").isNull()) {
|
||||||
|
config.m_specialHandshakeTimeout = obj.value("Itime").toString();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ QJsonObject InterfaceConfig::toJson() const {
|
|||||||
(m_hopType == InterfaceConfig::SingleHop)) {
|
(m_hopType == InterfaceConfig::SingleHop)) {
|
||||||
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
||||||
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
||||||
json.insert("dnsServer", QJsonValue(m_dnsServer));
|
json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer));
|
||||||
|
json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray allowedIPAddesses;
|
QJsonArray allowedIPAddesses;
|
||||||
@@ -48,6 +49,13 @@ QJsonObject InterfaceConfig::toJson() const {
|
|||||||
}
|
}
|
||||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||||
|
|
||||||
|
|
||||||
|
QJsonArray jsAllowedDnsServers;
|
||||||
|
for (const QString& i : m_allowedDnsServers) {
|
||||||
|
jsAllowedDnsServers.append(QJsonValue(i));
|
||||||
|
}
|
||||||
|
json.insert("allowedDnsServers", jsAllowedDnsServers);
|
||||||
|
|
||||||
QJsonArray disabledApps;
|
QJsonArray disabledApps;
|
||||||
for (const QString& i : m_vpnDisabledApps) {
|
for (const QString& i : m_vpnDisabledApps) {
|
||||||
disabledApps.append(QJsonValue(i));
|
disabledApps.append(QJsonValue(i));
|
||||||
@@ -93,11 +101,15 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||||||
out << "MTU = " << m_deviceMTU << "\n";
|
out << "MTU = " << m_deviceMTU << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_dnsServer.isNull()) {
|
if (!m_primaryDnsServer.isEmpty()) {
|
||||||
QStringList dnsServers(m_dnsServer);
|
QStringList dnsServers;
|
||||||
|
dnsServers.append(m_primaryDnsServer);
|
||||||
|
if (!m_secondaryDnsServer.isEmpty()) {
|
||||||
|
dnsServers.append(m_secondaryDnsServer);
|
||||||
|
}
|
||||||
// If the DNS is not the Gateway, it's a user defined DNS
|
// If the DNS is not the Gateway, it's a user defined DNS
|
||||||
// thus, not add any other :)
|
// thus, not add any other :)
|
||||||
if (m_dnsServer == m_serverIpv4Gateway) {
|
if (m_primaryDnsServer == m_serverIpv4Gateway) {
|
||||||
dnsServers.append(m_serverIpv6Gateway);
|
dnsServers.append(m_serverIpv6Gateway);
|
||||||
}
|
}
|
||||||
out << "DNS = " << dnsServers.join(", ") << "\n";
|
out << "DNS = " << dnsServers.join(", ") << "\n";
|
||||||
@@ -118,6 +130,12 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||||||
if (!m_responsePacketJunkSize.isNull()) {
|
if (!m_responsePacketJunkSize.isNull()) {
|
||||||
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
||||||
}
|
}
|
||||||
|
if (!m_cookieReplyPacketJunkSize.isNull()) {
|
||||||
|
out << "S3 = " << m_cookieReplyPacketJunkSize << "\n";
|
||||||
|
}
|
||||||
|
if (!m_transportPacketJunkSize.isNull()) {
|
||||||
|
out << "S4 = " << m_transportPacketJunkSize << "\n";
|
||||||
|
}
|
||||||
if (!m_initPacketMagicHeader.isNull()) {
|
if (!m_initPacketMagicHeader.isNull()) {
|
||||||
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
||||||
}
|
}
|
||||||
@@ -131,6 +149,16 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
|||||||
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const QString& key : m_specialJunk.keys()) {
|
||||||
|
out << key << " = " << m_specialJunk[key] << "\n";
|
||||||
|
}
|
||||||
|
for (const QString& key : m_controlledJunk.keys()) {
|
||||||
|
out << key << " = " << m_controlledJunk[key] << "\n";
|
||||||
|
}
|
||||||
|
if (!m_specialHandshakeTimeout.isNull()) {
|
||||||
|
out << "Itime = " << m_specialHandshakeTimeout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
// If any extra config was provided, append it now.
|
// If any extra config was provided, append it now.
|
||||||
for (const QString& key : extra.keys()) {
|
for (const QString& key : extra.keys()) {
|
||||||
out << key << " = " << extra[key] << "\n";
|
out << key << " = " << extra[key] << "\n";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#define INTERFACECONFIG_H
|
#define INTERFACECONFIG_H
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "ipaddress.h"
|
#include "ipaddress.h"
|
||||||
@@ -31,12 +32,14 @@ class InterfaceConfig {
|
|||||||
QString m_serverIpv4AddrIn;
|
QString m_serverIpv4AddrIn;
|
||||||
QString m_serverPskKey;
|
QString m_serverPskKey;
|
||||||
QString m_serverIpv6AddrIn;
|
QString m_serverIpv6AddrIn;
|
||||||
QString m_dnsServer;
|
QString m_primaryDnsServer;
|
||||||
|
QString m_secondaryDnsServer;
|
||||||
int m_serverPort = 0;
|
int m_serverPort = 0;
|
||||||
int m_deviceMTU = 1420;
|
int m_deviceMTU = 1420;
|
||||||
QList<IPAddress> m_allowedIPAddressRanges;
|
QList<IPAddress> m_allowedIPAddressRanges;
|
||||||
QStringList m_excludedAddresses;
|
QStringList m_excludedAddresses;
|
||||||
QStringList m_vpnDisabledApps;
|
QStringList m_vpnDisabledApps;
|
||||||
|
QStringList m_allowedDnsServers;
|
||||||
bool m_killSwitchEnabled;
|
bool m_killSwitchEnabled;
|
||||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||||
QString m_installationId;
|
QString m_installationId;
|
||||||
@@ -47,10 +50,15 @@ class InterfaceConfig {
|
|||||||
QString m_junkPacketMaxSize;
|
QString m_junkPacketMaxSize;
|
||||||
QString m_initPacketJunkSize;
|
QString m_initPacketJunkSize;
|
||||||
QString m_responsePacketJunkSize;
|
QString m_responsePacketJunkSize;
|
||||||
|
QString m_cookieReplyPacketJunkSize;
|
||||||
|
QString m_transportPacketJunkSize;
|
||||||
QString m_initPacketMagicHeader;
|
QString m_initPacketMagicHeader;
|
||||||
QString m_responsePacketMagicHeader;
|
QString m_responsePacketMagicHeader;
|
||||||
QString m_underloadPacketMagicHeader;
|
QString m_underloadPacketMagicHeader;
|
||||||
QString m_transportPacketMagicHeader;
|
QString m_transportPacketMagicHeader;
|
||||||
|
QMap<QString, QString> m_specialJunk;
|
||||||
|
QMap<QString, QString> m_controlledJunk;
|
||||||
|
QString m_specialHandshakeTimeout;
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
QString toWgConf(
|
QString toWgConf(
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_4_34)">
|
||||||
|
<path d="M55.5 12.3333H18.5C15.0942 12.3333 12.3333 15.0943 12.3333 18.5V55.5C12.3333 58.9058 15.0942 61.6667 18.5 61.6667H55.5C58.9057 61.6667 61.6666 58.9058 61.6666 55.5V18.5C61.6666 15.0943 58.9057 12.3333 55.5 12.3333Z" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M21.5833 24.6667H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M21.5833 37H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M21.5833 49.3333H40.0833" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="61.5" cy="12.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_4_34">
|
||||||
|
<rect width="74" height="74" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 982 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#CBCAC8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<!-- Основа газеты -->
|
||||||
|
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||||||
|
<!-- Линии текста -->
|
||||||
|
<line x1="7" y1="8" x2="17" y2="8"/>
|
||||||
|
<line x1="7" y1="12" x2="17" y2="12"/>
|
||||||
|
<line x1="7" y1="16" x2="13" y2="16"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 410 B |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="17.5" cy="17.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 188 B |
@@ -32,17 +32,41 @@
|
|||||||
<false/>
|
<false/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>AmneziaVPNLaunchScreen</string>
|
<string>AmneziaVPNLaunchScreen</string>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
<true/>
|
||||||
|
<key>UISceneConfigurations</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIWindowSceneSessionRoleApplication</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneClassName</key>
|
||||||
|
<string>UIWindowScene</string>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>Default Configuration</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>QIOSWindowSceneDelegate</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array/>
|
<array/>
|
||||||
<key>UIRequiresFullScreen</key>
|
<key>UIRequiresFullScreen</key>
|
||||||
<true/>
|
<false/>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<array/>
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
<key>UIUserInterfaceStyle</key>
|
<key>UIUserInterfaceStyle</key>
|
||||||
<string>Light</string>
|
<string>Light</string>
|
||||||
<key>com.wireguard.ios.app_group_id</key>
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
|||||||
@@ -26,10 +26,22 @@ set_target_properties(networkextension PROPERTIES
|
|||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
|||||||
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
<?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>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
|
|
||||||
<key>CFBundleAllowMixedLocalizations</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
|
||||||
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
||||||
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(MARKETING_VERSION)</string>
|
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.utilities</string>
|
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
|
||||||
|
|
||||||
<key>LSMultipleInstancesProhibited</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
|
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -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,34 +2,40 @@
|
|||||||
<!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>
|
<key>com.apple.developer.networking.custom-protocol</key>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(APP_ID_MACOS)</string>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>app-proxy-provider</string>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
|
<string>dns-settings</string>
|
||||||
|
<string>relay</string>
|
||||||
|
<string>content-filter-provider</string>
|
||||||
|
<string>dns-proxy</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.developer.system-extension.install</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.developer.networking.vpn.api</key>
|
||||||
|
<array>
|
||||||
|
<string>allow-vpn</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<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>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.network.server</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,41 +2,30 @@
|
|||||||
<!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>
|
<key>com.apple.developer.networking.custom-protocol</key>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>dns-settings</string>
|
||||||
|
<string>relay</string>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
|
<string>content-filter-provider</string>
|
||||||
|
<string>dns-proxy</string>
|
||||||
|
<string>app-proxy-provider</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.developer.networking.vpn.api</key>
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
<string>allow-vpn</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>$(DEVELOPMENT_TEAM)</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.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>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.private.network.socket-delegate</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
enable_language(Swift)
|
||||||
|
message("Client message >> macos build >> AmneziaVPNNetworkExtension")
|
||||||
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
|
||||||
|
add_executable(AmneziaVPNNetworkExtension)
|
||||||
|
|
||||||
|
message("executable_path is: @executable_path/../../Frameworks")
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
|
||||||
|
# MACOSX_BUNDLE YES
|
||||||
|
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_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
message("DEPLOY is ON")
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN.NE"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.amneziaVPN.NE"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension 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("AmneziaVPNNetworkExtension" 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}/../..)
|
||||||
|
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${FW_LIBRESOLV})
|
||||||
|
|
||||||
|
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||||
|
target_compile_options(AmneziaVPNNetworkExtension 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(AmneziaVPNNetworkExtension 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(AmneziaVPNNetworkExtension PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET AmneziaVPNNetworkExtension 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(AmneziaVPNNetworkExtension PRIVATE
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||||
|
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/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(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||||
|
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension 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>
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
* 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
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#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();
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
|
|
||||||
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
||||||
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
||||||
|
QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray();
|
||||||
|
|
||||||
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||||
|
|
||||||
@@ -148,7 +149,14 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
|
||||||
|
json.insert("primaryDnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||||
|
|
||||||
|
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
||||||
|
if (!rawConfig.value(amnezia::config_key::dns1).toString().
|
||||||
|
contains(amnezia::protocols::dns::amneziaDnsIp)) {
|
||||||
|
json.insert("secondaryDnsServer", rawConfig.value(amnezia::config_key::dns2));
|
||||||
|
}
|
||||||
|
|
||||||
QJsonArray jsAllowedIPAddesses;
|
QJsonArray jsAllowedIPAddesses;
|
||||||
|
|
||||||
@@ -226,6 +234,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
|
|
||||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||||
|
|
||||||
|
json.insert("allowedDnsServers", allowedDns);
|
||||||
|
|
||||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||||
|
|
||||||
if (protocolName == amnezia::config_key::awg) {
|
if (protocolName == amnezia::config_key::awg) {
|
||||||
@@ -234,28 +244,61 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||||
|
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||||
|
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||||
|
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||||
|
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||||
|
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||||
|
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||||
|
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||||
|
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||||
|
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||||
|
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||||
|
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||||
} else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined()
|
} else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
|
||||||
|
// && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
|
||||||
|
// && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) {
|
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
|
||||||
|
/* && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::specialJunk5).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
|
||||||
|
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()*/) {
|
||||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||||
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
|
||||||
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
|
||||||
|
// json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
|
||||||
|
// json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
|
||||||
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
|
||||||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||||
|
// json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
|
||||||
|
// json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
|
||||||
|
// json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
|
||||||
|
// json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
|
||||||
|
// json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
|
||||||
|
// json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
|
||||||
|
// json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
|
||||||
|
// json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
|
||||||
|
// json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
write(json);
|
write(json);
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
#include <dispatch/dispatch.h>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "ios_controller.h"
|
||||||
|
|
||||||
|
using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *);
|
||||||
|
|
||||||
|
static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr;
|
||||||
|
|
||||||
|
static void amnezia_handleURL(NSURL *url)
|
||||||
|
{
|
||||||
|
if (!url || !url.isFileURL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filePath(url.path.UTF8String);
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
if (filePath.contains("backup")) {
|
||||||
|
IosController::Instance()->importBackupFromOutside(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray data = file.readAll();
|
||||||
|
IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet<UIOpenURLContext *> *contexts)
|
||||||
|
{
|
||||||
|
if (g_originalSceneOpenURLContexts) {
|
||||||
|
g_originalSceneOpenURLContexts(self, _cmd, scene, contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contexts || contexts.count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
for (UIOpenURLContext *context in contexts) {
|
||||||
|
amnezia_handleURL(context.URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface AmneziaSceneDelegateHooks : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AmneziaSceneDelegateHooks
|
||||||
|
|
||||||
|
+ (void)load
|
||||||
|
{
|
||||||
|
Class cls = objc_getClass("QIOSWindowSceneDelegate");
|
||||||
|
if (!cls) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEL selector = @selector(scene:openURLContexts:);
|
||||||
|
Method method = class_getInstanceMethod(cls, selector);
|
||||||
|
if (method) {
|
||||||
|
g_originalSceneOpenURLContexts = reinterpret_cast<SceneOpenURLContexts>(method_getImplementation(method));
|
||||||
|
method_setImplementation(method, reinterpret_cast<IMP>(amnezia_scene_openURLContexts));
|
||||||
|
} else {
|
||||||
|
const char *types = "v@:@@";
|
||||||
|
class_addMethod(cls, selector, reinterpret_cast<IMP>(amnezia_scene_openURLContexts), types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -2,7 +2,8 @@ import Foundation
|
|||||||
import os.log
|
import os.log
|
||||||
|
|
||||||
struct Log {
|
struct Log {
|
||||||
static let osLog = Logger()
|
private static let subsystemIdentifier = Bundle.main.bundleIdentifier ?? "org.amnezia.AmneziaVPN"
|
||||||
|
static let osLog = Logger(subsystem: subsystemIdentifier, category: "App")
|
||||||
|
|
||||||
private static let IsLoggingEnabledKey = "IsLoggingEnabled"
|
private static let IsLoggingEnabledKey = "IsLoggingEnabled"
|
||||||
static var isLoggingEnabled: Bool {
|
static var isLoggingEnabled: Bool {
|
||||||
@@ -77,9 +78,40 @@ struct Log {
|
|||||||
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
|
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
|
||||||
NSLog("\(title) \(message)")
|
NSLog("\(title) \(message)")
|
||||||
|
|
||||||
guard isLoggingEnabled else { return }
|
switch type {
|
||||||
|
case .debug:
|
||||||
|
if title.isEmpty {
|
||||||
|
osLog.debug("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
osLog.debug("\(title, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .info:
|
||||||
|
if title.isEmpty {
|
||||||
|
osLog.info("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
osLog.info("\(title, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .error:
|
||||||
|
if title.isEmpty {
|
||||||
|
osLog.error("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
osLog.error("\(title, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .fault:
|
||||||
|
if title.isEmpty {
|
||||||
|
osLog.fault("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
osLog.fault("\(title, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if title.isEmpty {
|
||||||
|
osLog.log("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
osLog.log("\(title, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
osLog.log(level: type, "\(title) \(message)")
|
guard isLoggingEnabled else { return }
|
||||||
|
|
||||||
let date = Date()
|
let date = Date()
|
||||||
let level = Record.Level(from: type)
|
let level = Record.Level(from: type)
|
||||||
|
|||||||
@@ -1,22 +1,76 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
|
|
||||||
|
private let subsystemIdentifier = Bundle.main.bundleIdentifier ?? "org.amnezia.AmneziaVPN"
|
||||||
|
private let wireGuardSystemLogger = Logger(subsystem: subsystemIdentifier, category: "WireGuard")
|
||||||
|
private let openVPNSystemLogger = Logger(subsystem: subsystemIdentifier, category: "OpenVPN")
|
||||||
|
private let xraySystemLogger = Logger(subsystem: subsystemIdentifier, category: "Xray")
|
||||||
|
private let networkExtensionLogger = Logger(subsystem: subsystemIdentifier, category: "NetworkExtension")
|
||||||
|
|
||||||
|
private func logToSystem(_ logger: Logger, type: OSLogType, prefix: String, title: String, message: String) {
|
||||||
|
let combinedTitle: String
|
||||||
|
if title.isEmpty {
|
||||||
|
combinedTitle = prefix
|
||||||
|
} else {
|
||||||
|
combinedTitle = "\(prefix): \(title)"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch type {
|
||||||
|
case .debug:
|
||||||
|
if combinedTitle.isEmpty {
|
||||||
|
logger.debug("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
logger.debug("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .info:
|
||||||
|
if combinedTitle.isEmpty {
|
||||||
|
logger.info("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
logger.info("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .error:
|
||||||
|
if combinedTitle.isEmpty {
|
||||||
|
logger.error("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
logger.error("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
case .fault:
|
||||||
|
if combinedTitle.isEmpty {
|
||||||
|
logger.fault("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
logger.fault("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if combinedTitle.isEmpty {
|
||||||
|
logger.log("\(message, privacy: .public)")
|
||||||
|
} else {
|
||||||
|
logger.log("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func wg_log(_ type: OSLogType, title: String = "", staticMessage: StaticString) {
|
public func wg_log(_ type: OSLogType, title: String = "", staticMessage: StaticString) {
|
||||||
neLog(type, title: "WG: \(title)", message: "\(staticMessage)")
|
let stringMessage = String(describing: staticMessage)
|
||||||
|
logToSystem(wireGuardSystemLogger, type: type, prefix: "WG", title: title, message: stringMessage)
|
||||||
|
neLog(type, title: "WG: \(title)", message: stringMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func wg_log(_ type: OSLogType, title: String = "", message: String) {
|
public func wg_log(_ type: OSLogType, title: String = "", message: String) {
|
||||||
|
logToSystem(wireGuardSystemLogger, type: type, prefix: "WG", title: title, message: message)
|
||||||
neLog(type, title: "WG: \(title)", message: message)
|
neLog(type, title: "WG: \(title)", message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
|
public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
|
||||||
|
logToSystem(openVPNSystemLogger, type: type, prefix: "OVPN", title: title, message: message)
|
||||||
neLog(type, title: "OVPN: \(title)", message: message)
|
neLog(type, title: "OVPN: \(title)", message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func xrayLog(_ type: OSLogType, title: String = "", message: String) {
|
public func xrayLog(_ type: OSLogType, title: String = "", message: String) {
|
||||||
|
logToSystem(xraySystemLogger, type: type, prefix: "XRAY", title: title, message: message)
|
||||||
neLog(type, title: "XRAY: \(title)", message: message)
|
neLog(type, title: "XRAY: \(title)", message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func neLog(_ type: OSLogType, title: String = "", message: String) {
|
public func neLog(_ type: OSLogType, title: String = "", message: String) {
|
||||||
|
logToSystem(networkExtensionLogger, type: type, prefix: "NE", title: title, message: message)
|
||||||
Log.log(type, title: "NE: \(title)", message: message)
|
Log.log(type, title: "NE: \(title)", message: message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import OpenVPNAdapter
|
import OpenVPNAdapter
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
struct OpenVPNConfig: Decodable {
|
struct OpenVPNConfig: Decodable {
|
||||||
let config: String
|
let config: String
|
||||||
@@ -27,26 +28,83 @@ extension PacketTunnelProvider {
|
|||||||
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
|
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
|
||||||
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
||||||
} catch {
|
} catch {
|
||||||
ovpnLog(.error, message: "Can't parse config: \(error.localizedDescription)")
|
ovpnLog(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)")
|
||||||
|
|
||||||
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
|
|
||||||
ovpnLog(.error, message: "Can't parse config: \(underlyingError.localizedDescription)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func logOpenVPNError(_ error: NSError) {
|
||||||
|
let fatalFlag = (error.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool) ?? false
|
||||||
|
var lines: [String] = []
|
||||||
|
lines.append("domain=\(error.domain) code=\(error.code) fatal=\(fatalFlag)")
|
||||||
|
|
||||||
|
if let adapterMessage = error.userInfo[OpenVPNAdapterErrorMessageKey] as? String, !adapterMessage.isEmpty {
|
||||||
|
lines.append("message=\(adapterMessage)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let userInfoKeys = error.userInfo.keys.map { String(describing: $0) }.sorted()
|
||||||
|
if !userInfoKeys.isEmpty {
|
||||||
|
lines.append("userInfoKeys=[\(userInfoKeys.joined(separator: ","))]")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let underlying = error.userInfo[NSUnderlyingErrorKey] as? NSError {
|
||||||
|
lines.append("underlying=\(underlying.domain)#\(underlying.code) fatal=\((underlying.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool) ?? false)")
|
||||||
|
if let underlyingMessage = underlying.userInfo[OpenVPNAdapterErrorMessageKey] as? String, !underlyingMessage.isEmpty {
|
||||||
|
lines.append("underlyingMessage=\(underlyingMessage)")
|
||||||
|
} else if !underlying.localizedDescription.isEmpty {
|
||||||
|
lines.append("underlyingLocalized=\(underlying.localizedDescription)")
|
||||||
|
}
|
||||||
|
} else if let underlying = error.userInfo[NSUnderlyingErrorKey] {
|
||||||
|
lines.append("underlyingRaw=\(underlying)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let formatted = lines.joined(separator: "\n ")
|
||||||
|
ovpnLog(.error, title: "Error", message: formatted)
|
||||||
|
}
|
||||||
|
|
||||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
|
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
|
||||||
withShadowSocks viaSS: Bool = false,
|
withShadowSocks viaSS: Bool = false,
|
||||||
completionHandler: @escaping (Error?) -> Void) {
|
completionHandler: @escaping (Error?) -> Void) {
|
||||||
ovpnLog(.info, message: "Setup and launch")
|
ovpnLog(.info, message: "Setup and launch")
|
||||||
|
|
||||||
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
var configString = String(decoding: ovpnConfiguration, as: UTF8.self)
|
||||||
|
|
||||||
|
let digest = SHA256.hash(data: ovpnConfiguration)
|
||||||
|
let digestString = digest.map { String(format: "%02x", $0) }.joined()
|
||||||
|
ovpnLog(.info, title: "ConfigDigest", message: digestString)
|
||||||
|
|
||||||
|
let hasTlsAuthOpen = configString.contains("<tls-auth>")
|
||||||
|
let hasTlsAuthClose = configString.contains("</tls-auth>")
|
||||||
|
ovpnLog(.info, title: "ConfigFlags", message: "tls-auth open=\(hasTlsAuthOpen) close=\(hasTlsAuthClose)")
|
||||||
|
|
||||||
|
let lines = configString.split(separator: "\n")
|
||||||
|
let head = lines.prefix(10).joined(separator: "\n")
|
||||||
|
let tail = lines.suffix(10).joined(separator: "\n")
|
||||||
|
ovpnLog(.debug, title: "ConfigHead", message: head)
|
||||||
|
ovpnLog(.debug, title: "ConfigTail", message: tail)
|
||||||
|
|
||||||
|
if let start = configString.range(of: "<tls-auth>"),
|
||||||
|
let end = configString.range(of: "</tls-auth>", range: start.upperBound..<configString.endIndex) {
|
||||||
|
let keyBody = String(configString[start.upperBound..<end.lowerBound])
|
||||||
|
ovpnLog(.debug, title: "TLSAuthInline", message: keyBody)
|
||||||
|
let sanitizedLines = keyBody
|
||||||
|
.split(whereSeparator: { $0.isNewline })
|
||||||
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||||
|
.filter { !$0.isEmpty }
|
||||||
|
.filter { !$0.hasPrefix("#") }
|
||||||
|
|
||||||
|
let sanitizedKey = sanitizedLines.joined(separator: "\n")
|
||||||
|
ovpnLog(.debug, title: "TLSAuthSanitized", message: sanitizedKey)
|
||||||
|
let sanitizedBlock = "<tls-auth>\n\(sanitizedKey)\n</tls-auth>"
|
||||||
|
configString.replaceSubrange(start.lowerBound..<end.upperBound, with: sanitizedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalizedConfig = configString.replacingOccurrences(of: "\r\n", with: "\n")
|
||||||
|
let sanitizedData = Data(normalizedConfig.utf8)
|
||||||
|
|
||||||
let configuration = OpenVPNConfiguration()
|
let configuration = OpenVPNConfiguration()
|
||||||
configuration.fileContent = ovpnConfiguration
|
configuration.fileContent = sanitizedData
|
||||||
if str.contains("cloak") {
|
if configString.contains("cloak") {
|
||||||
configuration.setPTCloak()
|
configuration.setPTCloak()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +115,8 @@ extension PacketTunnelProvider {
|
|||||||
evaluation = try ovpnAdapter?.apply(configuration: configuration)
|
evaluation = try ovpnAdapter?.apply(configuration: configuration)
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
|
let nsError = error as NSError
|
||||||
|
ovpnLog(.error, title: "ApplyConfig", message: "domain=\(nsError.domain) code=\(nsError.code) info=\(nsError.userInfo)")
|
||||||
completionHandler(error)
|
completionHandler(error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,8 +268,11 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||||||
|
|
||||||
// Handle errors thrown by the OpenVPN library
|
// Handle errors thrown by the OpenVPN library
|
||||||
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
|
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
|
||||||
|
let nsError = error as NSError
|
||||||
|
logOpenVPNError(nsError)
|
||||||
|
|
||||||
// Handle only fatal errors
|
// Handle only fatal errors
|
||||||
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
|
guard let fatal = nsError.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
|
||||||
fatal == true else { return }
|
fatal == true else { return }
|
||||||
|
|
||||||
if vpnReachability.isTracking {
|
if vpnReachability.isTracking {
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -90,3 +100,4 @@ struct ProtectionPair {
|
|||||||
textField.removeFromSuperview()
|
textField.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ struct WGConfig: Decodable {
|
|||||||
let initPacketMagicHeader, responsePacketMagicHeader: String?
|
let initPacketMagicHeader, responsePacketMagicHeader: String?
|
||||||
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
||||||
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
||||||
let initPacketJunkSize, responsePacketJunkSize: String?
|
let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: String?
|
||||||
|
let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String?
|
||||||
|
let controlledJunk1, controlledJunk2, controlledJunk3: String?
|
||||||
|
let specialHandshakeTimeout: String?
|
||||||
let dns1: String
|
let dns1: String
|
||||||
let dns2: String
|
let dns2: String
|
||||||
let mtu: String
|
let mtu: String
|
||||||
@@ -23,7 +26,10 @@ struct WGConfig: Decodable {
|
|||||||
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
|
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
|
||||||
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
||||||
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
||||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2", cookieReplyPacketJunkSize = "S3", transportPacketJunkSize = "S4"
|
||||||
|
case specialJunk1 = "I1", specialJunk2 = "I2", specialJunk3 = "I3", specialJunk4 = "I4", specialJunk5 = "I5"
|
||||||
|
case controlledJunk1 = "J1", controlledJunk2 = "J2", controlledJunk3 = "J3"
|
||||||
|
case specialHandshakeTimeout = "Itime"
|
||||||
case dns1
|
case dns1
|
||||||
case dns2
|
case dns2
|
||||||
case mtu
|
case mtu
|
||||||
@@ -40,19 +46,59 @@ struct WGConfig: Decodable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var settings: String {
|
var settings: String {
|
||||||
junkPacketCount == nil ? "" :
|
guard junkPacketCount != nil else { return "" }
|
||||||
"""
|
|
||||||
Jc = \(junkPacketCount!)
|
|
||||||
Jmin = \(junkPacketMinSize!)
|
|
||||||
Jmax = \(junkPacketMaxSize!)
|
|
||||||
S1 = \(initPacketJunkSize!)
|
|
||||||
S2 = \(responsePacketJunkSize!)
|
|
||||||
H1 = \(initPacketMagicHeader!)
|
|
||||||
H2 = \(responsePacketMagicHeader!)
|
|
||||||
H3 = \(underloadPacketMagicHeader!)
|
|
||||||
H4 = \(transportPacketMagicHeader!)
|
|
||||||
|
|
||||||
"""
|
var settingsLines: [String] = []
|
||||||
|
|
||||||
|
// Required parameters when junkPacketCount is present
|
||||||
|
settingsLines.append("Jc = \(junkPacketCount!)")
|
||||||
|
settingsLines.append("Jmin = \(junkPacketMinSize!)")
|
||||||
|
settingsLines.append("Jmax = \(junkPacketMaxSize!)")
|
||||||
|
settingsLines.append("S1 = \(initPacketJunkSize!)")
|
||||||
|
settingsLines.append("S2 = \(responsePacketJunkSize!)")
|
||||||
|
|
||||||
|
settingsLines.append("H1 = \(initPacketMagicHeader!)")
|
||||||
|
settingsLines.append("H2 = \(responsePacketMagicHeader!)")
|
||||||
|
settingsLines.append("H3 = \(underloadPacketMagicHeader!)")
|
||||||
|
settingsLines.append("H4 = \(transportPacketMagicHeader!)")
|
||||||
|
|
||||||
|
// Optional parameters - only add if not nil and not empty
|
||||||
|
if let s3 = cookieReplyPacketJunkSize, !s3.isEmpty {
|
||||||
|
settingsLines.append("S3 = \(s3)")
|
||||||
|
}
|
||||||
|
if let s4 = transportPacketJunkSize, !s4.isEmpty {
|
||||||
|
settingsLines.append("S4 = \(s4)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let i1 = specialJunk1, !i1.isEmpty {
|
||||||
|
settingsLines.append("I1 = \(i1)")
|
||||||
|
}
|
||||||
|
if let i2 = specialJunk2, !i2.isEmpty {
|
||||||
|
settingsLines.append("I2 = \(i2)")
|
||||||
|
}
|
||||||
|
if let i3 = specialJunk3, !i3.isEmpty {
|
||||||
|
settingsLines.append("I3 = \(i3)")
|
||||||
|
}
|
||||||
|
if let i4 = specialJunk4, !i4.isEmpty {
|
||||||
|
settingsLines.append("I4 = \(i4)")
|
||||||
|
}
|
||||||
|
if let i5 = specialJunk5, !i5.isEmpty {
|
||||||
|
settingsLines.append("I5 = \(i5)")
|
||||||
|
}
|
||||||
|
if let j1 = controlledJunk1, !j1.isEmpty {
|
||||||
|
settingsLines.append("J1 = \(j1)")
|
||||||
|
}
|
||||||
|
if let j2 = controlledJunk2, !j2.isEmpty {
|
||||||
|
settingsLines.append("J2 = \(j2)")
|
||||||
|
}
|
||||||
|
if let j3 = controlledJunk3, !j3.isEmpty {
|
||||||
|
settingsLines.append("J3 = \(j3)")
|
||||||
|
}
|
||||||
|
if let itime = specialHandshakeTimeout, !itime.isEmpty {
|
||||||
|
settingsLines.append("Itime = \(itime)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsLines.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var str: String {
|
var str: String {
|
||||||
|
|||||||
@@ -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,15 +27,51 @@ 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];
|
UIApplication *application = [UIApplication sharedApplication];
|
||||||
for (UIWindow *window in windows) {
|
|
||||||
if (window.isKeyWindow) {
|
if (@available(iOS 13.0, *)) {
|
||||||
|
for (UIScene *scene in application.connectedScenes) {
|
||||||
|
if (scene.activationState != UISceneActivationStateForegroundActive) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![scene isKindOfClass:[UIWindowScene class]]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||||
|
|
||||||
|
for (UIWindow *window in windowScene.windows) {
|
||||||
|
if (window.isKeyWindow && window.rootViewController) {
|
||||||
return window.rootViewController;
|
return window.rootViewController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (UIWindow *window in windowScene.windows) {
|
||||||
|
if (!window.isHidden && window.rootViewController) {
|
||||||
|
return window.rootViewController;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UIWindow *window in application.windows) {
|
||||||
|
if (window.isKeyWindow && window.rootViewController) {
|
||||||
|
return window.rootViewController;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UIWindow *window in application.windows) {
|
||||||
|
if (window.rootViewController) {
|
||||||
|
return window.rootViewController;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -249,6 +285,21 @@ 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 !MACOS_NE
|
||||||
|
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.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
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;
|
||||||
@@ -507,6 +558,8 @@ bool IosController::setupWireGuard()
|
|||||||
|
|
||||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||||
|
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||||
|
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||||
|
|
||||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||||
@@ -605,11 +658,23 @@ bool IosController::setupAwg()
|
|||||||
|
|
||||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||||
|
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
|
||||||
|
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
|
||||||
|
|
||||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||||
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]);
|
||||||
|
wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]);
|
||||||
|
wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]);
|
||||||
|
wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]);
|
||||||
|
wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]);
|
||||||
|
wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]);
|
||||||
|
wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]);
|
||||||
|
wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]);
|
||||||
|
wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]);
|
||||||
|
|
||||||
QJsonDocument wgConfigDoc(wgConfig);
|
QJsonDocument wgConfigDoc(wgConfig);
|
||||||
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
@@ -789,14 +854,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();
|
||||||
@@ -809,6 +874,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
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 +883,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];
|
||||||
@@ -827,8 +894,9 @@ QString IosController::openFile() {
|
|||||||
|
|
||||||
[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 +905,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,6 +26,7 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
@implementation DocumentPickerDelegate
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
@@ -43,3 +44,4 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||