Compare commits

..

10 Commits

Author SHA1 Message Date
ronoaer 111cc8dcbb implemented the function check for updates and online installer for the platform linux 2023-11-02 02:09:30 -07:00
ronoaer 3d118e0c31 fixed online updates and installer 2023-10-31 21:19:36 +08:00
ronoaer feeb9e4809 for testing check-for-updtes on platform macos 2023-10-31 17:21:51 +08:00
ronoaer 92d49bd725 Merge pull request #408 from amnezia-vpn/update_to_r2
Upload builds to CloudFlare R2
2023-10-31 10:24:52 +08:00
tiaga 506df2eb89 Upload builds to CloudFlare R2
Upload unpacked builds for MacOS to CloudFlare R2 when changes are pushed to the `dev` branch. In addition, improve `.gitignore` for VSCode.
2023-10-31 00:39:39 +07:00
ronoaer 5991e0e597 fixed the syntax error 2023-10-29 22:24:30 +08:00
ronoaer ba424d0ac6 fixed the compiling error of platform ios 2023-10-29 21:58:44 +08:00
ronoaer 5b2b675c53 fixed compiling error 2023-10-29 17:58:16 +08:00
ronoaer 9441830a47 added check-for-updates for platform macos 2023-10-29 16:31:23 +08:00
ronoaer fbdae95802 created macos-online-installer and repository for v4 2023-10-26 21:43:45 +08:00
67 changed files with 1154 additions and 2191 deletions
-1
View File
@@ -3,4 +3,3 @@ deploy/data/windows/x64/tap/windows_10/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf
client/3rd/* linguist-vendored
client/android/gradlew.bat eol=crlf
+54 -88
View File
@@ -1,12 +1,6 @@
name: 'Deploy workflow'
on:
push:
branches:
- '**'
env:
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
on: [push]
jobs:
Build-Linux-Ubuntu:
@@ -30,7 +24,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
extra: '--external 7z'
- name: 'Get sources'
uses: actions/checkout@v3
@@ -94,7 +88,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
extra: '--external 7z'
- name: 'Setup mvsc'
uses: ilammy/msvc-dev-cmd@v1
@@ -124,14 +118,15 @@ jobs:
# ------------------------------------------------------
Build-iOS:
name: 'Build-iOS'
Build-IOS:
name: 'Build-IOS'
runs-on: macos-12
env:
QT_VERSION: 6.5.2
steps:
# Just select XCode
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
@@ -147,7 +142,6 @@ jobs:
arch: 'clang_64'
dir: ${{ runner.temp }}
set-env: 'true'
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install iOS Qt'
uses: jurplel/install-qt-action@v3
@@ -159,7 +153,7 @@ jobs:
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
extra: '--external 7z'
- name: 'Install go'
uses: actions/setup-go@v3
@@ -179,7 +173,7 @@ jobs:
- name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2
- name: 'Install dependencies'
- name: Install dependencies
run: pip install jsonschema jinja2
- name: 'Build project'
@@ -237,7 +231,7 @@ jobs:
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
extra: '--external 7z'
- name: 'Get sources'
uses: actions/checkout@v3
@@ -254,12 +248,21 @@ jobs:
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh
- name: 'Upload upacked build to update server'
# if: github.ref_name == 'dev'
env:
MACOS_UNPACKED_BUILD_PATH: deploy/build/installer/amneziavpn-macos-repository
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
run: aws s3 sync --endpoint-url https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com ${{ env.MACOS_UNPACKED_BUILD_PATH }} s3://updates/beta/macos --delete
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN_MacOS_installer
path: AmneziaVPN.dmg
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
with:
@@ -273,10 +276,21 @@ jobs:
name: 'Build-Android'
runs-on: ubuntu-latest
strategy:
matrix:
include:
- abi: 'x86_64'
qt_arch: 'android_x86_64'
- abi: 'x86'
qt_arch: 'android_x86'
- abi: 'armeabi-v7a'
qt_arch: 'android_armv7'
- abi: 'arm64-v8a'
qt_arch: 'android_arm64_v8a'
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.5.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
QT_VERSION: 6.5.2
ANDROID_BUILD_PLATFORM: android-33
steps:
- name: 'Install desktop Qt'
@@ -286,58 +300,29 @@ jobs:
host: 'linux'
target: 'desktop'
arch: 'gcc_64'
modules: ${{ env.QT_MODULES }}
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
- name: 'Install android_x86_64 Qt'
- name: 'Install android Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
arch: ${{ matrix.qt_arch }}
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
- name: 'Grant execute permission for qt-cmake'
shell: bash
run: |
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake
- name: 'Get sources'
uses: actions/checkout@v3
@@ -351,14 +336,15 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
java-version: '11'
cache: 'gradle'
- name: 'Setup Android NDK'
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: 'r26b'
ndk-version: 'r25c'
local-cache: 'true'
- name: 'Decode keystore secret to file'
env:
@@ -371,36 +357,16 @@ jobs:
env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash
run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload x86_64 apk'
- name: 'Upload apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-x86_64
path: deploy/build/AmneziaVPN-x86_64-release.apk
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
name: AmneziaVPN-android-${{ matrix.abi }}
path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk
retention-days: 7
-64
View File
@@ -1,64 +0,0 @@
name: 'Upload a new version'
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
jobs:
upload:
runs-on: ubuntu-latest
name: upload
steps:
- name: Checkout CMakeLists.txt
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
sparse-checkout: |
CMakeLists.txt
sparse-checkout-cone-mode: false
- name: Verify git tag
run: |
GIT_TAG=${{ github.ref_name }}
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
else
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
exit 1
fi
- name: Download artifacts from the "${{ github.ref_name }}" tag
uses: robinraju/release-downloader@v1.8
with:
tag: ${{ github.ref_name }}
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
out-file-path: ${{ github.ref_name }}
- name: Upload beta version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'dev')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: beta/${{ github.ref_name }}
- name: Upload stable version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'master')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: stable/${{ github.ref_name }}
+1 -1
View File
@@ -9,7 +9,7 @@ deploy/build_32/*
deploy/build_64/*
winbuild*.bat
.cache/
.vscode/
# Qt-es
/.qmake.cache
+1 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.1.0.1
project(${PROJECT} VERSION 4.0.8.6
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,6 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 39)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
+11 -3
View File
@@ -288,8 +288,6 @@ void AmneziaApplication::initModels()
&ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
&ServersModel::updateContainersConfig);
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@@ -298,7 +296,17 @@ void AmneziaApplication::initModels()
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
connect(m_containersModel.get(), &ContainersModel::defaultContainerChanged, this, [this]() {
if ((m_containersModel->getDefaultContainer() == DockerContainer::WireGuard
|| m_containersModel->getDefaultContainer() == DockerContainer::Awg)
&& m_sitesModel->isSplitTunnelingEnabled()) {
m_sitesModel->toggleSplitTunneling(false);
emit m_pageController->showNotificationMessage(
tr("Split tunneling for %1 is not implemented, the option was disabled")
.arg(ContainerProps::containerHumanNames().value(m_containersModel->getDefaultContainer())));
}
});
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
+100 -91
View File
@@ -1,52 +1,60 @@
<?xml version="1.0"?>
<!-- Leave package attribute for androiddeployqt -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
package="org.amnezia.vpn"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:installLocation="auto">
<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.autofocus" android:required="false" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<!-- The following comment will be replaced upon deployment with default features based on the dependencies
of the application. Remove the comment if you do not require these default features. -->
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:anyDensity="true"
android:smallScreens="true"/>
<application
android:name=".qt.AmneziaApp"
android:hardwareAccelerated="true"
android:label="-- %%INSERT_APP_NAME%% --"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:icon="@drawable/icon"
android:roundIcon="@drawable/icon_round"
android:theme="@style/Theme.AppCompat.NoActionBar">
android:roundIcon="@drawable/icon_round">
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name=".qt.VPNActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<!-- android:theme="@style/splashScreenTheme"-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" />
@@ -54,70 +62,73 @@
<meta-data
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>
<meta-data
android:name="android.app.background_running"
android:value="false"/>
<meta-data
android:name="android.app.arguments"
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
</activity>
<activity
android:name=".qt.CameraActivity"
android:exported="false" />
<activity
android:name=".qt.ImportConfigActivity"
android:exported="true">
android:exported="true" >
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.vpn" />
<data android:pathPattern=".*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn" />
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.cfg" />
<data android:pathPattern=".*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg" />
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.conf" />
<data android:pathPattern=".*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf" />
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
</intent-filter>
</activity>
@@ -125,32 +136,30 @@
android:name=".VPNService"
android:process=":QtOnlyProcess"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
<service
android:name=".qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
</service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.amnezia.vpn.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider" />
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
</provider>
</application>
+152 -4
View File
@@ -1,5 +1,153 @@
// dummy file for androiddeployqt
apply plugin: 'com.github.ben-manes.versions'
buildscript {
ext{
kotlin_version = "1.7.22"
// for libwg
appcompatVersion = '1.1.0'
annotationsVersion = '1.0.1'
databindingVersion = '3.3.1'
jsr305Version = '3.0.2'
streamsupportVersion = '1.7.0'
threetenabpVersion = '1.1.1'
groupName = 'org.amnezia.vpn'
minSdkVer = '24'
cmakeMinVersion = "3.25.0+"
}
repositories {
google()
jcenter()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation group: 'org.json', name: 'json', version: '20220924'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation project(path: ':shadowsocks')
// CameraX core library using the camera2 implementation
def camerax_version = "1.2.1"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("androidx.camera:camera-extensions:${camerax_version}")
def camerax_ml_version = "1.2.0-beta02"
def ml_kit_version = "17.0.3"
implementation("androidx.camera:camera-mlkit-vision:${camerax_ml_version}")
implementation("com.google.mlkit:barcode-scanning:${ml_kit_version}")
}
androidExtensions {
experimental = true
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
// Extract native libraries from the APK
packagingOptions.jniLibs.useLegacyPackaging true
dexOptions {
javaMaxHeapSize "3g"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 37 // Change to a higher number
versionName "4.0.8" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}
}
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging
-92
View File
@@ -1,92 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("property-delegate")
}
kotlin {
jvmToolchain(17)
}
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
buildConfig = true
viewBinding = true
}
androidResources {
// don't compress Qt binary resources file
noCompress += "rcc"
}
packaging {
// compress .so binary libraries
jniLibs.useLegacyPackaging = true
}
defaultConfig {
applicationId = "org.amnezia.vpn"
targetSdk = qtTargetSdkVersion.toInt()
// keeps language resources for only the locales specified below
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
}
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("$qtAndroidDir/src", "src"))
res.setSrcDirs(listOf("$qtAndroidDir/res", "res"))
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
}
signingConfigs {
register("release") {
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
}
}
buildTypes {
release {
// exclude coroutine debug resource from release build
packaging {
resources.excludes += "DebugProbesKt.bin"
}
signingConfig = signingConfigs["release"]
}
}
splits {
abi {
isEnable = true
reset()
include(*qtTargetAbiList.split(',').toTypedArray())
isUniversalApk = false
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(project(":shadowsocks"))
// todo: remove after finish refactoring
implementation(libs.androidx.constraintlayout)
}
+22 -41
View File
@@ -1,46 +1,27 @@
# Specifies the JVM arguments used for the daemon process
org.gradle.jvmargs=-Xms512m -Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError \
-Dfile.encoding=UTF-8
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
# Project-wide Gradle settings.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
# Use AndroidX library instead of a Support Library
android.useAndroidX=true
# Disable adding android:testOnly attribute to the manifest
android.injected.testOnly=false
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Disable providing custom values to resources from buildscript by default
android.defaults.buildfeatures.resvalues=false
# Disable compileShaders tasks by default
android.defaults.buildfeatures.shaders=false
# Disable Android resource processing for libraries by default
android.library.defaults.buildfeatures.androidresources=false
# Qt variables
# At build time androiddeployqt replaces these values with:
# androidCompileSdkVersion - androiddeployqt --android-platform parameter
# androidBuildToolsVersion - QT_ANDROID_SDK_BUILD_TOOLS_REVISION cmake target parameter
# qtMinSdkVersion - QT_ANDROID_MIN_SDK_VERSION cmake target parameter
# qtTargetSdkVersion - QT_ANDROID_TARGET_SDK_VERSION cmake target parameter
# androidNdkVersion - version from ANDROID_NDK_ROOT environment variable
# qtTargetAbiList - qt-cmake QT_ANDROID_ABIS parameter
# qtAndroidDir - path to qt binding java source code
# buildDir - hardcoded "build" value in androiddeployqt
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
#qtAndroidDir=/QT_BASE/android_ABI/src/android/java
#buildDir=build
# Note about qtAndroidDir:
# Some IDEs (for example, IntelliJ IDEA) may index all data from a common root of the project and qtAndroidDir.
# Therefore, it's recommended to copy qt android files to a directory inside the project
# and specify the path to that directory in qtAndroidDir.
android.bundle.enableUncompressedNativeLibs=false
androidBuildToolsVersion=30.0.2
androidCompileSdkVersion=30
org.gradle.caching=true
org.gradle.parallel=true
android.enableJetifier=true
android.injected.testOnly=false
kapt.use.worker.api=false
kapt.incremental.apt=false
-35
View File
@@ -1,35 +0,0 @@
[versions]
agp = "8.1.3"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-appcompat = "1.6.1"
androidx-camera = "1.2.3"
androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
# todo: remove after finish refactoring
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
[bundles]
androidx-camera = [
"androidx-camera-core",
"androidx-camera-lifecycle",
"androidx-camera-view",
"androidx-camera-camera2"
]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -1,25 +0,0 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
register("settingsGradlePropertyDelegate") {
id = "settings-property-delegate"
implementationClass = "SettingsPropertyDelegate"
}
register("projectGradlePropertyDelegate") {
id = "property-delegate"
implementationClass = "ProjectPropertyDelegate"
}
}
}
@@ -1,49 +0,0 @@
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.Properties
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.provider.ProviderFactory
private fun localProperties(rootDir: File) = Properties().apply {
val localProperties = File(rootDir, "local.properties")
if (localProperties.isFile) {
InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use {
load(it)
}
}
}
private class PropertyDelegate(
rootDir: File,
private val providers: ProviderFactory,
private val localProperties: Properties = localProperties(rootDir)
) : ReadOnlyProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name)
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
private lateinit var projectPropertyDelegate: ReadOnlyProperty<Any?, String>
class SettingsPropertyDelegate : Plugin<Settings> {
override fun apply(settings: Settings) {
settingsPropertyDelegate = PropertyDelegate(settings.rootDir, settings.providers)
}
}
class ProjectPropertyDelegate : Plugin<Project> {
override fun apply(project: Project) {
projectPropertyDelegate = PropertyDelegate(project.rootDir, project.providers)
}
}
val Settings.gradleProperties: ReadOnlyProperty<Any?, String>
get() = settingsPropertyDelegate
val Project.gradleProperties: ReadOnlyProperty<Any?, String>
get() = projectPropertyDelegate
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+115 -192
View File
@@ -1,127 +1,78 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#!/usr/bin/env sh
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -130,120 +81,92 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+24 -32
View File
@@ -1,20 +1,4 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,23 +9,19 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -55,7 +35,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -65,26 +45,38 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
+21
View File
@@ -0,0 +1,21 @@
pluginManagement {
repositories {
google()
mavenCentral()
jcenter()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
}
include ':shadowsocks'
-58
View File
@@ -1,58 +0,0 @@
import com.android.build.api.dsl.SettingsExtension
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
includeBuild("./gradle/plugins")
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
}
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.1.3"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":shadowsocks")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties
val androidCompileSdkVersion: String by gradleProperties
val androidNdkVersion: String by gradleProperties
val qtMinSdkVersion: String by gradleProperties
// set default values for all modules
configure<SettingsExtension> {
buildToolsVersion = androidBuildToolsVersion
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
minSdk = qtMinSdkVersion.toInt()
ndkVersion = androidNdkVersion
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
allTasks.forEach { it.enabled = false }
}
}
+12 -10
View File
@@ -1,17 +1,14 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
//apply plugin: 'com.novoda.bintray-release'
android {
buildFeatures {
aidl true
androidResources true
}
compileSdkVersion 30
defaultConfig {
namespace "org.amnezia.vpn.shadowsocks.core"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0.0"
@@ -33,6 +30,10 @@ android {
}
}
androidExtensions {
experimental = true
}
//def lifecycleVersion = '2.0.0'
def roomVersion = "2.4.3"
//def preferencexVersion = '1.0.0'
@@ -45,9 +46,10 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.room:room-runtime:$roomVersion" // runtime
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1"
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn.shadowsocks.core"
tools:ignore="MissingLeanbackLauncher">
<uses-feature
@@ -65,7 +65,6 @@ class VpnRequestActivity : AppCompatActivity() {
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show()
}
finish()
super.onActivityResult(requestCode, resultCode, data)
}
override fun onDestroy() {
@@ -28,7 +28,6 @@ import kotlinx.coroutines.channels.sendBlocking
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.channels.*
import kotlinx.coroutines.channels.trySendBlocking
class ChannelMonitor : Thread("ChannelMonitor") {
private data class Registration(val channel: SelectableChannel,
@@ -53,7 +52,7 @@ class ChannelMonitor : Thread("ChannelMonitor") {
registerInternal(this, SelectionKey.OP_READ) {
val junk = ByteBuffer.allocateDirect(1)
while (read(junk) > 0) {
pendingRegistrations.tryReceive().getOrNull()!!.apply {
pendingRegistrations.poll()!!.apply {
try {
result.complete(registerInternal(channel, ops, listener))
} catch (e: Exception) {
@@ -113,7 +112,7 @@ class ChannelMonitor : Thread("ChannelMonitor") {
(key.attachment() as (SelectionKey) -> Unit)(key)
}
}
closeChannel.trySendBlocking(Unit)
closeChannel.sendBlocking(Unit)
}
fun close(scope: CoroutineScope) {
@@ -33,7 +33,6 @@ import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.launch
import java.io.File
import java.io.IOException
import kotlinx.coroutines.channels.trySendBlocking
abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name) {
private val localSocket = LocalSocket().apply {
@@ -61,7 +60,7 @@ abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name
}
}
}
closeChannel.trySendBlocking(Unit)
closeChannel.sendBlocking(Unit)
}
open fun shutdown(scope: CoroutineScope) {
@@ -1,509 +0,0 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
/**
* Class that represents a range of IP addresses. This range could be a proper subnet, but that's
* not necessarily the case (see {@code getPrefix} and {@code toSubnets}).
*/
public class IPRange implements Comparable<IPRange>
{
private final byte[] mBitmask = { (byte)0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
private byte[] mFrom;
private byte[] mTo;
private Integer mPrefix;
/**
* Determine if the range is a proper subnet and, if so, what the network prefix is.
*/
private void determinePrefix()
{
boolean matching = true;
mPrefix = mFrom.length * 8;
for (int i = 0; i < mFrom.length; i++)
{
for (int bit = 0; bit < 8; bit++)
{
if (matching)
{
if ((mFrom[i] & mBitmask[bit]) != (mTo[i] & mBitmask[bit]))
{
mPrefix = (i * 8) + bit;
matching = false;
}
}
else
{
if ((mFrom[i] & mBitmask[bit]) != 0 || (mTo[i] & mBitmask[bit]) == 0)
{
mPrefix = null;
return;
}
}
}
}
}
private IPRange(byte[] from, byte[] to)
{
mFrom = from;
mTo = to;
determinePrefix();
}
public IPRange(String from, String to) throws UnknownHostException
{
this(Utils.parseInetAddress(from), Utils.parseInetAddress(to));
}
public IPRange(InetAddress from, InetAddress to)
{
initializeFromRange(from, to);
}
private void initializeFromRange(InetAddress from, InetAddress to)
{
byte[] fa = from.getAddress(), ta = to.getAddress();
if (fa.length != ta.length)
{
throw new IllegalArgumentException("Invalid range");
}
if (compareAddr(fa, ta) < 0)
{
mFrom = fa;
mTo = ta;
}
else
{
mTo = fa;
mFrom = ta;
}
determinePrefix();
}
public IPRange(String base, int prefix) throws UnknownHostException
{
this(Utils.parseInetAddress(base), prefix);
}
public IPRange(InetAddress base, int prefix)
{
this(base.getAddress(), prefix);
}
private IPRange(byte[] from, int prefix)
{
initializeFromCIDR(from, prefix);
}
private void initializeFromCIDR(byte[] from, int prefix)
{
if (from.length != 4 && from.length != 16)
{
throw new IllegalArgumentException("Invalid address");
}
if (prefix < 0 || prefix > from.length * 8)
{
throw new IllegalArgumentException("Invalid prefix");
}
byte[] to = from.clone();
byte mask = (byte)(0xff << (8 - prefix % 8));
int i = prefix / 8;
if (i < from.length)
{
from[i] = (byte)(from[i] & mask);
to[i] = (byte)(to[i] | ~mask);
Arrays.fill(from, i+1, from.length, (byte)0);
Arrays.fill(to, i+1, to.length, (byte)0xff);
}
mFrom = from;
mTo = to;
mPrefix = prefix;
}
public IPRange(String cidr) throws UnknownHostException
{
/* only verify the basic structure */
if (!cidr.matches("(?i)^(([0-9.]+)|([0-9a-f:]+))(-(([0-9.]+)|([0-9a-f:]+))|(/\\d+))?$"))
{
throw new IllegalArgumentException("Invalid CIDR or range notation");
}
if (cidr.contains("-"))
{
String[] parts = cidr.split("-");
InetAddress from = InetAddress.getByName(parts[0]);
InetAddress to = InetAddress.getByName(parts[1]);
initializeFromRange(from, to);
}
else
{
String[] parts = cidr.split("/");
InetAddress addr = InetAddress.getByName(parts[0]);
byte[] base = addr.getAddress();
int prefix = base.length * 8;
if (parts.length > 1)
{
prefix = Integer.parseInt(parts[1]);
}
initializeFromCIDR(base, prefix);
}
}
/**
* Returns the first address of the range. The network ID in case this is a proper subnet.
*/
public InetAddress getFrom()
{
try
{
return InetAddress.getByAddress(mFrom);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* Returns the last address of the range.
*/
public InetAddress getTo()
{
try
{
return InetAddress.getByAddress(mTo);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* If this range is a proper subnet returns its prefix, otherwise returns null.
*/
public Integer getPrefix()
{
return mPrefix;
}
@Override
public int compareTo(@NonNull IPRange other)
{
int cmp = compareAddr(mFrom, other.mFrom);
if (cmp == 0)
{ /* smaller ranges first */
cmp = compareAddr(mTo, other.mTo);
}
return cmp;
}
@Override
public boolean equals(Object o)
{
if (o == null || !(o instanceof IPRange))
{
return false;
}
return this == o || compareTo((IPRange)o) == 0;
}
@Override
public String toString()
{
try
{
if (mPrefix != null)
{
return InetAddress.getByAddress(mFrom).getHostAddress() + "/" + mPrefix;
}
return InetAddress.getByAddress(mFrom).getHostAddress() + "-" +
InetAddress.getByAddress(mTo).getHostAddress();
}
catch (UnknownHostException ignored)
{
return super.toString();
}
}
private int compareAddr(byte a[], byte b[])
{
if (a.length != b.length)
{
return (a.length < b.length) ? -1 : 1;
}
for (int i = 0; i < a.length; i++)
{
if (a[i] != b[i])
{
if (((int)a[i] & 0xff) < ((int)b[i] & 0xff))
{
return -1;
}
else
{
return 1;
}
}
}
return 0;
}
/**
* Check if this range fully contains the given range.
*/
public boolean contains(IPRange range)
{
return compareAddr(mFrom, range.mFrom) <= 0 && compareAddr(range.mTo, mTo) <= 0;
}
/**
* Check if this and the given range overlap.
*/
public boolean overlaps(IPRange range)
{
return !(compareAddr(mTo, range.mFrom) < 0 || compareAddr(range.mTo, mFrom) < 0);
}
private byte[] dec(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (--addr[i] != (byte)0xff)
{
break;
}
}
return addr;
}
private byte[] inc(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (++addr[i] != 0)
{
break;
}
}
return addr;
}
/**
* Remove the given range from the current range. Returns a list of resulting ranges (these are
* not proper subnets). At most two ranges are returned, in case the given range is contained in
* this but does not equal it, which would result in an empty list (which is also the case if
* this range is fully contained in the given range).
*/
public List<IPRange> remove(IPRange range)
{
ArrayList<IPRange> list = new ArrayList<>();
if (!overlaps(range))
{ /* | this | or | this |
* | range | | range | */
list.add(this);
}
else if (!range.contains(this))
{ /* we are not completely removed, so none of these cases applies:
* | this | or | this | or | this |
* | range | | range | | range | */
if (compareAddr(mFrom, range.mFrom) < 0 && compareAddr(range.mTo, mTo) < 0)
{ /* the removed range is completely within our boundaries:
* | this |
* | range | */
list.add(new IPRange(mFrom, dec(range.mFrom.clone())));
list.add(new IPRange(inc(range.mTo.clone()), mTo));
}
else
{ /* one end is within our boundaries the other at or outside it:
* | this | or | this | or | this | or | this |
* | range | | range | | range | | range | */
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : inc(range.mTo.clone());
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : dec(range.mFrom.clone());
list.add(new IPRange(from, to));
}
}
return list;
}
private boolean adjacent(IPRange range)
{
if (compareAddr(mTo, range.mFrom) < 0)
{
byte[] to = inc(mTo.clone());
return compareAddr(to, range.mFrom) == 0;
}
byte[] from = dec(mFrom.clone());
return compareAddr(from, range.mTo) == 0;
}
/**
* Merge two adjacent or overlapping ranges, returns null if it's not possible to merge them.
*/
public IPRange merge(IPRange range)
{
if (overlaps(range))
{
if (contains(range))
{
return this;
}
else if (range.contains(this))
{
return range;
}
}
else if (!adjacent(range))
{
return null;
}
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : range.mFrom;
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : range.mTo;
return new IPRange(from, to);
}
/**
* Split the given range into a sorted list of proper subnets.
*/
public List<IPRange> toSubnets()
{
ArrayList<IPRange> list = new ArrayList<>();
if (mPrefix != null)
{
list.add(this);
}
else
{
int i = 0, bit = 0, prefix, netmask, common_byte, common_bit;
int from_cur, from_prev = 0, to_cur, to_prev = 1;
boolean from_full = true, to_full = true;
byte[] from = mFrom.clone();
byte[] to = mTo.clone();
/* find a common prefix */
while (i < from.length && (from[i] & mBitmask[bit]) == (to[i] & mBitmask[bit]))
{
if (++bit == 8)
{
bit = 0;
i++;
}
}
prefix = i * 8 + bit;
/* at this point we know that the addresses are either equal, or that the
* current bits in the 'from' and 'to' addresses are 0 and 1, respectively.
* we now look at the rest of the bits as two binary trees (0=left, 1=right)
* where 'from' and 'to' are both leaf nodes. all leaf nodes between these
* nodes are addresses contained in the range. to collect them as subnets
* we follow the trees from both leaf nodes to their root node and record
* all complete subtrees (right for from, left for to) we come across as
* subnets. in that process host bits are zeroed out. if both addresses
* are equal we won't enter the loop below.
* 0_____|_____1 for the 'from' address we assume we start on a
* 0__|__ 1 0__|__1 left subtree (0) and follow the left edges until
* _|_ _|_ _|_ _|_ we reach the root of this subtree, which is
* | | | | | | | | either the root of this whole 'from'-subtree
* 0 1 0 1 0 1 0 1 (causing us to leave the loop) or the root node
* of the right subtree (1) of another node (which actually could be the
* leaf node we start from). that whole subtree gets recorded as subnet.
* next we follow the right edges to the root of that subtree which again is
* either the 'from'-root or the root node in the left subtree (0) of
* another node. the complete right subtree of that node is the next subnet
* we record. from there we assume that we are in that right subtree and
* recursively follow right edges to its root. for the 'to' address the
* procedure is exactly the same but with left and right reversed.
*/
if (++bit == 8)
{
bit = 0;
i++;
}
common_byte = i;
common_bit = bit;
netmask = from.length * 8;
for (i = from.length - 1; i >= common_byte; i--)
{
int bit_min = (i == common_byte) ? common_bit : 0;
for (bit = 7; bit >= bit_min; bit--)
{
byte mask = mBitmask[bit];
from_cur = from[i] & mask;
if (from_prev == 0 && from_cur != 0)
{ /* 0 -> 1: subnet is the whole current (right) subtree */
list.add(new IPRange(from.clone(), netmask));
from_full = false;
}
else if (from_prev != 0 && from_cur == 0)
{ /* 1 -> 0: invert bit to switch to right subtree and add it */
from[i] ^= mask;
list.add(new IPRange(from.clone(), netmask));
from_cur = 1;
}
/* clear the current bit */
from[i] &= ~mask;
from_prev = from_cur;
to_cur = to[i] & mask;
if (to_prev != 0 && to_cur == 0)
{ /* 1 -> 0: subnet is the whole current (left) subtree */
list.add(new IPRange(to.clone(), netmask));
to_full = false;
}
else if (to_prev == 0 && to_cur != 0)
{ /* 0 -> 1: invert bit to switch to left subtree and add it */
to[i] ^= mask;
list.add(new IPRange(to.clone(), netmask));
to_cur = 0;
}
/* clear the current bit */
to[i] &= ~mask;
to_prev = to_cur;
netmask--;
}
}
if (from_full && to_full)
{ /* full subnet (from=to or from=0.. and to=1.. after common prefix) - not reachable
* due to the shortcut at the top */
list.add(new IPRange(from.clone(), prefix));
}
else if (from_full)
{ /* full from subnet (from=0.. after prefix) */
list.add(new IPRange(from.clone(), prefix + 1));
}
else if (to_full)
{ /* full to subnet (to=1.. after prefix) */
list.add(new IPRange(to.clone(), prefix + 1));
}
}
Collections.sort(list);
return list;
}
}
@@ -1,223 +0,0 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
/**
* Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
* modifying the set and enumerating the resulting subnets.
*/
public class IPRangeSet implements Iterable<IPRange>
{
private TreeSet<IPRange> mRanges = new TreeSet<>();
/**
* Parse the given string (space separated ranges in CIDR or range notation) and return the
* resulting set or {@code null} if the string was invalid. An empty set is returned if the given string
* is {@code null}.
*/
public static IPRangeSet fromString(String ranges)
{
IPRangeSet set = new IPRangeSet();
if (ranges != null)
{
for (String range : ranges.split("\\s+"))
{
try
{
set.add(new IPRange(range));
}
catch (Exception unused)
{ /* besides due to invalid strings exceptions might get thrown if the string
* contains a hostname (NetworkOnMainThreadException) */
return null;
}
}
}
return set;
}
/**
* Add a range to this set. Automatically gets merged with existing ranges.
*/
public void add(IPRange range)
{
if (mRanges.contains(range))
{
return;
}
reinsert:
while (true)
{
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
IPRange replacement = existing.merge(range);
if (replacement != null)
{
iterator.remove();
range = replacement;
continue reinsert;
}
}
mRanges.add(range);
break;
}
}
/**
* Add all ranges from the given set.
*/
public void add(IPRangeSet ranges)
{
if (ranges == this)
{
return;
}
for (IPRange range : ranges.mRanges)
{
add(range);
}
}
/**
* Add all ranges from the given collection to this set.
*/
public void addAll(Collection<? extends IPRange> coll)
{
for (IPRange range : coll)
{
add(range);
}
}
/**
* Remove the given range from this set. Existing ranges are automatically adjusted.
*/
public void remove(IPRange range)
{
ArrayList <IPRange> additions = new ArrayList<>();
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
List<IPRange> result = existing.remove(range);
if (result.size() == 0)
{
iterator.remove();
}
else if (!result.get(0).equals(existing))
{
iterator.remove();
additions.addAll(result);
}
}
mRanges.addAll(additions);
}
/**
* Remove the given ranges from ranges in this set.
*/
public void remove(IPRangeSet ranges)
{
if (ranges == this)
{
mRanges.clear();
return;
}
for (IPRange range : ranges.mRanges)
{
remove(range);
}
}
/**
* Get all the subnets derived from all the ranges in this set.
*/
public Iterable<IPRange> subnets()
{
return new Iterable<IPRange>()
{
@Override
public Iterator<IPRange> iterator()
{
return new Iterator<IPRange>()
{
private Iterator<IPRange> mIterator = mRanges.iterator();
private List<IPRange> mSubnets;
@Override
public boolean hasNext()
{
return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
}
@Override
public IPRange next()
{
if (mSubnets == null || mSubnets.size() == 0)
{
IPRange range = mIterator.next();
mSubnets = range.toSubnets();
}
return mSubnets.remove(0);
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public Iterator<IPRange> iterator()
{
return mRanges.iterator();
}
/**
* Returns the number of ranges, not subnets.
*/
public int size()
{
return mRanges.size();
}
@Override
public String toString()
{ /* we could use TextUtils, but that causes the unit tests to fail */
StringBuilder sb = new StringBuilder();
for (IPRange range : mRanges)
{
if (sb.length() > 0)
{
sb.append(" ");
}
sb.append(range.toString());
}
return sb.toString();
}
}
@@ -31,7 +31,7 @@ public final class InetEndpoint {
private final boolean isResolved;
private final Object lock = new Object();
private final int port;
private long lastResolution;
private Instant lastResolution = Instant.EPOCH;
@Nullable private InetEndpoint resolved;
private InetEndpoint(final String host, final boolean isResolved, final int port) {
@@ -89,7 +89,7 @@ public final class InetEndpoint {
return Optional.of(this);
synchronized (lock) {
// TODO(zx2c4): Implement a real timeout mechanism using DNS TTL
if (System.currentTimeMillis() - lastResolution > 60000L) {
if (Duration.between(lastResolution, Instant.now()).toMinutes() > 1) {
try {
// Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues.
final InetAddress[] candidates = InetAddress.getAllByName(host);
@@ -101,7 +101,7 @@ public final class InetEndpoint {
}
}
resolved = new InetEndpoint(address.getHostAddress(), true, port);
lastResolution = System.currentTimeMillis();
lastResolution = Instant.now();
} catch (final UnknownHostException e) {
resolved = null;
}
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2014-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Utils
{
static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
/**
* Converts the given byte array to a hexadecimal string encoding.
*
* @param bytes byte array to convert
* @return hex string
*/
public static String bytesToHex(byte[] bytes)
{
char[] hex = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++)
{
int value = bytes[i];
hex[i*2] = HEXDIGITS[(value & 0xf0) >> 4];
hex[i*2+1] = HEXDIGITS[ value & 0x0f];
}
return new String(hex);
}
/**
* Validate the given proposal string
*
* @param ike true for IKE, false for ESP
* @param proposal proposal string
* @return true if valid
*/
public native static boolean isProposalValid(boolean ike, String proposal);
/**
* Parse an IP address without doing a name lookup
*
* @param address IP address string
* @return address bytes if valid
*/
private native static byte[] parseInetAddressBytes(String address);
/**
* Parse an IP address without doing a name lookup (as compared to InetAddress.fromName())
*
* @param address IP address string
* @return address if valid
* @throws UnknownHostException if address is invalid
*/
public static InetAddress parseInetAddress(String address) throws UnknownHostException
{
byte[] bytes = parseInetAddressBytes(address);
if (bytes == null)
{
throw new UnknownHostException();
}
return InetAddress.getByAddress(bytes);
}
}
@@ -12,7 +12,6 @@ import android.content.Intent
import android.os.Build
import android.os.Parcel
import androidx.core.app.NotificationCompat
import org.amnezia.vpn.shadowsocks.core.R
import org.json.JSONObject
object NotificationUtil {
@@ -103,7 +102,7 @@ object NotificationUtil {
val pendingIntent = PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
// Build our notification
sNotificationBuilder?.let {
it.setSmallIcon(R.drawable.ic_amnezia_round)
it.setSmallIcon(org.amnezia.vpn.R.drawable.ic_amnezia_round)
.setContentTitle(header)
.setContentText(message)
.setOnlyAlertOnce(true)
@@ -16,8 +16,6 @@ import com.wireguard.crypto.Key
import org.json.JSONObject
import java.util.Base64
import com.wireguard.config.*
import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event
@@ -74,8 +72,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
val jsonVpnConfig = mService.getVpnConfig()
val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config")
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
val resultingConfig = StringBuilder()
resultingConfig.append(ovpnConfig)
@@ -83,32 +79,29 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
if (jsonVpnConfig.getString("protocol") == "cloak") {
val cloakConfigJson: JSONObject = jsonVpnConfig.getJSONObject("cloak_config_data")
if (cloakConfigJson.has("NumConn")) {
if (cloakConfigJson.keySet().contains("NumConn")) {
cloakConfigJson.put("NumConn", 1)
}
if (cloakConfigJson.has("ProxyMethod")) {
if (cloakConfigJson.keySet().contains("ProxyMethod")) {
cloakConfigJson.put("ProxyMethod", "openvpn")
}
if (cloakConfigJson.has("port")) {
if (cloakConfigJson.keySet().contains("port")) {
val portValue = cloakConfigJson.get("port")
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", portValue)
}
if (cloakConfigJson.has("remote")) {
if (cloakConfigJson.keySet().contains("remote")) {
val hostValue = cloakConfigJson.get("remote")
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", hostValue)
}
val cloakConfigData = jsonVpnConfig.getJSONObject("cloak_config_data").toString().toByteArray()
val cloakConfig = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Base64.getEncoder().encodeToString(cloakConfigData)
} else {
android.util.Base64.encodeToString(cloakConfigData, android.util.Base64.DEFAULT)
}
val cloakConfig = Base64.getEncoder().encodeToString(
jsonVpnConfig.getJSONObject("cloak_config_data").toString().toByteArray()
)
resultingConfig.append("\n<cloak>\n")
resultingConfig.append(cloakConfig)
@@ -122,7 +115,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
eval_config(config)
val status = connect()
if (status.getError()) {
Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage())
}
@@ -147,31 +139,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
override fun tun_builder_establish(): Int {
Log.v(tag, "tun_builder_establish")
val jsonVpnConfig = mService.getVpnConfig()
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
if (splitTunnelType == 1) {
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val ipRange = IPRange(site)
mService.addRoute(ipRange.getFrom().getHostAddress(), ipRange.getPrefix())
}
}
if (splitTunnelType == 2) {
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
ipRangeSet.subnets().forEach {
mService.addRoute(it.getFrom().getHostAddress(), it.getPrefix())
Thread.sleep(10)
}
mService.addRoute("2000::", 3)
}
return mService.establish()!!.detachFd()
}
@@ -564,7 +564,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
return parseData
}
/**
* Create a Wireguard [Config] from a [json] string -
* The [json] will be created in AndroidVpnProtocol.cpp
@@ -572,67 +571,29 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun buildWireguardConfig(obj: JSONObject, type: String): Config {
val confBuilder = Config.Builder()
val wireguardConfigData = obj.getJSONObject(type)
val splitTunnelType = obj.getInt("splitTunnelType")
val splitTunnelSites = obj.getJSONArray("splitTunnelSites")
val config = parseConfigData(wireguardConfigData.getString("config"))
val peerBuilder = Peer.Builder()
val peerConfig = config["Peer"]!!
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
peerConfig["PresharedKey"]?.let { peerBuilder.setPreSharedKey(Key.fromBase64(it)) }
val allIpString = peerConfig["AllowedIPs"]
var allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
/* default value in template */
if (allIpString == "0.0.0.0/0, ::/0") {
allowedIPList = emptyList()
peerConfig["PresharedKey"]?.let {
peerBuilder.setPreSharedKey(Key.fromBase64(it))
}
if (allowedIPList.isEmpty() && (splitTunnelType == 0)) {
/* AllowedIP is empty and splitTunnel is turnoff */
/* use VPN for whole Internet */
val internetV4 = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV4)
val internetV6 = InetNetwork.parse("::/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV6)
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
if (allowedIPList.isEmpty()) {
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internet)
} else {
if (!allowedIPList.isEmpty()) {
/* We have predefined AllowedIP in WG config */
/* It's have higher priority than system SplitTunnel */
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
}
} else {
if (splitTunnelType == 1) {
/* Use system SplitTunnel */
/* VPN connection used only for defined IPs */
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val internet = InetNetwork.parse(site)
peerBuilder.addAllowedIp(internet)
}
}
if (splitTunnelType == 2) {
/* Use system SplitTunnel */
/* VPN connection used for all Internet exclude defined IPs */
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
val allowedIps = ipRangeSet.subnets().joinToString(", ") + ", 2000::/3"
peerBuilder.parseAllowedIPs(allowedIps)
}
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
}
}
val endpointConfig = peerConfig["Endpoint"]
val endpoint = InetEndpoint.parse(endpointConfig)
peerBuilder.setEndpoint(endpoint)
peerConfig["PersistentKeepalive"]?.let { peerBuilder.setPersistentKeepalive(it.toInt()) }
peerConfig["PersistentKeepalive"]?.let {
peerBuilder.setPersistentKeepalive(it.toInt())
}
confBuilder.addPeer(peerBuilder.build())
val ifaceBuilder = Interface.Builder()
@@ -642,7 +603,7 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceConfig["DNS"]!!.split(",").forEach {
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
}
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
if (type == "awg_config_data") {
ifaceBuilder.parseJc(ifaceConfig["Jc"])
@@ -663,13 +624,14 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceBuilder.parseH1("0")
ifaceBuilder.parseH2("0")
ifaceBuilder.parseH3("0")
ifaceBuilder.parseH4("0")
ifaceBuilder.parseH4("0")
}
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
(0 until jExcludedApplication.length()).toList().forEach {
(0 until jExcludedApplication.length()).toList().forEach {
val appName = jExcludedApplication.get(it).toString()
ifaceBuilder.excludeApplication(appName)
}*/
}*/
confBuilder.setInterface(ifaceBuilder.build())
return confBuilder.build()
@@ -784,13 +746,13 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun startWireGuard(type: String) {
val wireguard_conf = buildWireguardConfig(mConfig!!, type + "_config_data")
Log.i(tag, "startWireGuard: wireguard_conf : $wireguard_conf")
if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch
GoBackend.wgTurnOff(currentTunnelHandle)
}
val wgConfig: String = wireguard_conf.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
builder.setSession("Amnezia")
+33 -16
View File
@@ -1,20 +1,4 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 24)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
set_target_properties(${PROJECT} PROPERTIES
QT_ANDROID_VERSION_NAME ${CMAKE_PROJECT_VERSION}
QT_ANDROID_VERSION_CODE ${APP_ANDROID_VERSION_CODE}
QT_ANDROID_MIN_SDK_VERSION ${APP_ANDROID_MIN_SDK}
QT_ANDROID_TARGET_SDK_VERSION 34
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 34.0.0
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
)
set(QT_ANDROID_MULTI_ABI_FORWARD_VARS "QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL;CMAKE_BUILD_TYPE")
# We need to include qtprivate api's
# As QAndroidBinder is not yet implemented with a public api
set(LIBS ${LIBS} Qt6::CorePrivate)
@@ -39,6 +23,39 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
)
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/build.gradle
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.jar
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew.bat
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/res/values/libs.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/res/xml/fileprovider.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/AuthHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/IPCContract.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/NotificationUtil.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/Prefs.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNLogger.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNService.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNServiceBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET ${PROJECT} PROPERTY
QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android
)
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/android/${abi}/libwg.so
@@ -131,13 +131,10 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
config.append("block-ipv6\n");
}
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
}
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
+5 -2
View File
@@ -167,8 +167,11 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError;
}
runScript(credentials,
replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials,
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e;
}
+10 -70
View File
@@ -115,12 +115,8 @@ void LocalSocketController::daemonConnected() {
}
void LocalSocketController::activate(const QJsonObject &rawConfig) {
QString protocolName = rawConfig.value("protocol").toString();
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = rawConfig.value("splitTunnelSites").toArray();
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
QJsonObject json;
@@ -141,79 +137,23 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses;
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(","));
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config bacouse of higer priority
for (auto v : plainAllowedIP) {
QString ipRange = v.toString();
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
} else {
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
}
}
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses;
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
jsExcludedAddresses.append(ipRange);
}
}
json.insert("excludedAddresses", jsExcludedAddresses);
-2
View File
@@ -28,8 +28,6 @@ struct MessageKey
static const char *host;
static const char *port;
static const char *isOnDemand;
static const char *SplitTunnelType;
static const char *SplitTunnelSites;
};
class IosController : public QObject
+1 -18
View File
@@ -29,9 +29,6 @@ const char* MessageKey::errorCode = "errorCode";
const char* MessageKey::host = "host";
const char* MessageKey::port = "port";
const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) {
@@ -354,13 +351,6 @@ void IosController::startTunnel()
{
m_rxBytes = 0;
m_txBytes = 0;
int STT = m_rawConfig["splitTunnelType"].toInt();
QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray();
QJsonDocument doc;
doc.setArray(splitTunnelSites);
QString STS(doc.toJson());
[m_currentTunnel setEnabled:YES];
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
@@ -386,15 +376,8 @@ void IosController::startTunnel()
NSString *actionValue = [NSString stringWithUTF8String:Action::start];
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType];
NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT];
NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites];
NSString *SplitTunnelSitesValue = STS.toNSString();
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue,
SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue};
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue};
sendVpnExtensionMessage(message);
+75 -174
View File
@@ -15,7 +15,7 @@ struct Constants {
static let ovpnConfigKey = "ovpn"
static let wireGuardConfigKey = "wireguard"
static let loggerTag = "NET"
static let kActionStart = "start"
static let kActionRestart = "restart"
static let kActionStop = "stop"
@@ -29,8 +29,6 @@ struct Constants {
static let kMessageKeyHost = "host"
static let kMessageKeyPort = "port"
static let kMessageKeyOnDemand = "is-on-demand"
static let kMessageKeySplitTunnelType = "SplitTunnelType"
static let kMessageKeySplitTunnelSites = "SplitTunnelSites"
}
class PacketTunnelProvider: NEPacketTunnelProvider {
@@ -40,7 +38,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
wg_log(logLevel.osLogLevel, message: message)
}
}()
private lazy var ovpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
@@ -51,11 +49,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
private var openVPNConfig: Data? = nil
private var SplitTunnelType: String? = nil
private var SplitTunnelSites: String? = nil
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .none
@@ -67,34 +63,26 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
let tmpStr = String(data: messageData, encoding: .utf8)!
wg_log(.error, message: tmpStr)
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
Logger.global?.log(message: "Failed to serialize message from app")
return
}
guard let completionHandler = completionHandler else {
Logger.global?.log(message: "Missing message completion handler")
return
}
guard let action = message[Constants.kMessageKeyAction] as? String else {
Logger.global?.log(message: "Missing action key in app message")
completionHandler(nil)
return
}
if action == Constants.kActionStatus {
handleStatusAppMessage(messageData, completionHandler: completionHandler)
}
if action == Constants.kActionStart {
SplitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String
SplitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String
}
let callbackWrapper: (NSNumber?) -> Void = { errorCode in
//let tunnelId = self.tunnelConfig?.id ?? ""
let response: [String: Any] = [
@@ -102,11 +90,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
Constants.kMessageKeyErrorCode: errorCode ?? NSNull(),
Constants.kMessageKeyTunnelId: 0
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
}
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
dispatchQueue.async {
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
@@ -130,8 +118,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
switch self.protoType {
case .wireguard:
self.startWireguard(activationAttemptId: activationAttemptId,
errorNotifier: errorNotifier,
completionHandler: completionHandler)
errorNotifier: errorNotifier,
completionHandler: completionHandler)
case .openvpn:
self.startOpenVPN(completionHandler: completionHandler)
case .shadowsocks:
@@ -168,7 +156,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
case .shadowsocks:
break
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
case .none:
break
@@ -180,13 +168,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil)
return
}
let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil)
return
}
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
@@ -195,49 +182,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
if (tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ") == "0.0.0.0/0, ::/0") {
if (SplitTunnelType == "1") {
for index in tunnelConfiguration.peers.indices {
tunnelConfiguration.peers[index].allowedIPs.removeAll()
var allowedIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSArray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSArray {
if let allowedIP = IPAddressRange(from: allowedIPString) {
allowedIPs.append(allowedIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
}
} else {
if (SplitTunnelType == "2")
{
for index in tunnelConfiguration.peers.indices {
var excludeIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
excludeIPs.append(excludeIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
}
}
}
}
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
// Start the tunnel
@@ -248,30 +193,30 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
switch adapterError {
case .cannotLocateTunnelFileDescriptor:
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
case .dnsResolution(let dnsErrors):
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
.joined(separator: ", ")
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
case .setNetworkSettings(let error):
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
case .startWireGuardBackend(let errorCode):
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
case .invalidState:
// Must never happen
fatalError()
@@ -281,27 +226,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
// TODO: handle errors properly
wg_log(.error, message: "Can't start startOpenVPN()")
wg_log(.error, message: "Can't start startOpenVPN()")
return
}
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
}
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let error = error {
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
}
completionHandler()
#if os(macOS)
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
// Remove it when they finally fix this upstream and the fix has been rolled out to
@@ -318,7 +263,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
ovpnAdapter.disconnect()
}
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
wgAdapter.getRuntimeConfiguration { settings in
@@ -333,8 +278,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
for component in components{
let pair = component.components(separatedBy: "=")
if pair.count == 2 {
settingsDictionary[pair[0]] = pair[1]
}
settingsDictionary[pair[0]] = pair[1]
}
}
let response: [String: Any] = [
@@ -364,7 +309,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
@@ -373,7 +318,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings = settings {
@@ -389,76 +334,76 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
}
}
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
let bytesin = ovpnAdapter.transportStatistics.bytesIn
let bytesout = ovpnAdapter.transportStatistics.bytesOut
let response: [String: Any] = [
"rx_bytes" : bytesin,
"tx_bytes" : bytesout
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
let bytesin = ovpnAdapter.transportStatistics.bytesIn
let bytesout = ovpnAdapter.transportStatistics.bytesOut
let response: [String: Any] = [
"rx_bytes" : bytesin,
"tx_bytes" : bytesout
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
}
// TODO review
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
wg_log(.info, message: "setupAndlaunchOpenVPN")
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnConfiguration
if(str.contains("cloak")){
configuration.setPTCloak();
}
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try ovpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
if !evaluation.autologin {
wg_log(.info, message: "Implement login with user credentials")
}
vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return }
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
ovpnAdapter.connect(using: packetFlow)
// let ifaces = Interface.allInterfaces()
// .filter { $0.family == .ipv4 }
// .map { iface in iface.name }
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
// let ifaces = Interface.allInterfaces()
// .filter { $0.family == .ipv4 }
// .map { iface in iface.name }
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
}
// MARK: -- Network observing methods
private func startListeningForNetworkChanges() {
stopListeningForNetworkChanges()
addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil)
}
private func stopListeningForNetworkChanges() {
removeObserver(self, forKeyPath: Constants.kDefaultPathKey)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard Constants.kDefaultPathKey != keyPath else { return }
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
@@ -467,28 +412,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
let defPath = defaultPath,
lastPath != defPath || lastPath.description != defPath.description else {
return
}
return
}
DispatchQueue.main.async { [weak self] in
guard let `self` = self, self.defaultPath != nil else { return }
self.handle(networkChange: self.defaultPath!) { _ in }
}
}
private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) {
wg_log(.info, message: "Tunnel restarted.")
startTunnel(options: nil, completionHandler: completion)
}
private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
dispatchPrecondition(condition: .onQueue(dispatchQueue))
let emptyTunnelConfiguration = TunnelConfiguration(
name: nil,
interface: InterfaceConfiguration(privateKey: PrivateKey()),
peers: []
)
wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
self.dispatchQueue.async {
if let error {
@@ -500,9 +445,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
}
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
self.setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
@@ -533,50 +478,6 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""]
if (SplitTunnelType == "1") {
var ipv4IncludedRoutes = [NEIPv4Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSarray {
if let allowedIP = IPAddressRange(from: allowedIPString){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allowedIP.address)", subnetMask: "\(allowedIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
} else {
if (SplitTunnelType == "2")
{
var ipv4ExcludedRoutes = [NEIPv4Route]()
var ipv4IncludedRoutes = [NEIPv4Route]()
var ipv6IncludedRoutes = [NEIPv6Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
ipv4ExcludedRoutes.append(NEIPv4Route(destinationAddress: "\(excludeIP.address)", subnetMask: "\(excludeIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0"){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allIPv4.address)", subnetMask: "\(allIPv4.subnetMask())"))
}
if let allIPv6 = IPAddressRange(from: "::/0") {
ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(allIPv6.address)", networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength)))
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
}
}
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}
@@ -637,7 +538,7 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
wg_log(.info, message: logMessage)
}
}
extension WireGuardLogLevel {
var osLogLevel: OSLogType {
switch self {
@@ -158,15 +158,15 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
return false;
}
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 1);
}
if (rtm->rtm_type == RTN_THROW) {
struct in_addr ip4;
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4);
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
rtm->rtm_type = RTN_UNICAST;
int index = if_nametoindex(getgatewayandiface().toUtf8());
if (index <= 0) {
logger.error() << "if_nametoindex() failed:" << strerror(errno);
return false;
}
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
}
struct sockaddr_nl nladdr;
@@ -334,7 +334,7 @@ QString LinuxRouteMonitor::getgatewayandiface()
}
}
close(sock);
return gateway_address;
return interface;
}
static bool buildAllowedIp(wg_allowedip* ip,
@@ -236,17 +236,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
}
}
if (!config.m_excludedAddresses.empty()) {
for (const QString& i : config.m_excludedAddresses) {
logger.debug() << "range: " << i;
if (!allowTrafficToRange(i, HIGH_WEIGHT,
"Allow Ecxlude route", config.m_serverPublicKey)) {
return false;
}
}
}
result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
@@ -422,8 +411,8 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
}
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
int weight, const QString& title,
const QString& peer) {
int weight, const QString& title,
const QString& peer) {
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
GUID layerOut =
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
@@ -484,57 +473,6 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
return true;
}
bool WindowsFirewall::allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer) {
QString description("Allow traffic %1 %2 ");
auto lower = addr.address();
auto upper = addr.broadcastAddress();
const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol;
const GUID layerKeyOut =
isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
// Assemble the Filter base
FWPM_FILTER0 filter;
memset(&filter, 0, sizeof(filter));
filter.action.type = FWP_ACTION_PERMIT;
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = weight;
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
FWPM_FILTER_CONDITION0 cond[1] = {0};
FWP_RANGE0 ipRange;
QByteArray lowIpV6Buffer;
QByteArray highIpV6Buffer;
importAddress(lower, ipRange.valueLow, &lowIpV6Buffer);
importAddress(upper, ipRange.valueHigh, &highIpV6Buffer);
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
cond[0].matchType = FWP_MATCH_RANGE;
cond[0].conditionValue.type = FWP_RANGE_TYPE;
cond[0].conditionValue.rangeValue = &ipRange;
filter.numFilterConditions = 1;
filter.filterCondition = cond;
filter.layerKey = layerKeyOut;
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
peer)) {
return false;
}
filter.layerKey = layerKeyIn;
if (!enableFilter(&filter, title,
description.arg("from").arg(addr.toString()), peer)) {
return false;
}
return true;
}
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
// Allow outbound DHCPv4
{
@@ -52,9 +52,6 @@ class WindowsFirewall final : public QObject {
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
const QString& title, const QString& peer = QString());
bool allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer);
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
const QString& title);
bool allowDHCPTraffic(uint8_t weight, const QString& title);
-4
View File
@@ -43,7 +43,6 @@ namespace amnezia
constexpr char server_priv_key[] = "server_priv_key";
constexpr char server_pub_key[] = "server_pub_key";
constexpr char psk_key[] = "psk_key";
constexpr char allowed_ips[] = "allowed_ips";
constexpr char client_ip[] = "client_ip"; // internal ip address
@@ -79,9 +78,6 @@ namespace amnezia
constexpr char sftp[] = "sftp";
constexpr char awg[] = "awg";
constexpr char splitTunnelSites[] = "splitTunnelSites";
constexpr char splitTunnelType[] = "splitTunnelType";
}
namespace protocols
+190 -3
View File
@@ -16,6 +16,8 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
writeWireguardConfiguration(configuration);
// MZ
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
m_impl.reset(new LocalSocketController());
connect(m_impl.get(), &ControllerImpl::connected, this,
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
@@ -24,6 +26,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr);
#endif
}
WireguardProtocol::~WireguardProtocol()
@@ -34,10 +37,68 @@ WireguardProtocol::~WireguardProtocol()
void WireguardProtocol::stop()
{
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
stopMzImpl();
return;
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
qCritical() << "Wireguard executable missing!";
setLastError(ErrorCode::ExecutableMissing);
return;
}
m_wireguardStopProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStopProcess) {
qCritical() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->waitForSource(1000);
if (!m_wireguardStopProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStopProcess->setArguments(stopArgs());
qDebug() << stopArgs().join(" ");
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
if (result.returnValue()) {
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
} else {
qCritical() << "IPC client not initialized";
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
#endif
m_wireguardStopProcess->start();
m_wireguardStopProcess->waitForFinished(10000);
setConnectionState(Vpn::ConnectionState::Disconnected);
}
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode WireguardProtocol::startMzImpl()
{
m_impl->activate(m_rawConfig);
@@ -49,6 +110,7 @@ ErrorCode WireguardProtocol::stopMzImpl()
m_impl->deactivate();
return ErrorCode::NoError;
}
#endif
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
{
@@ -62,8 +124,21 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
m_configFile.close();
#if 0
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName());
if (result.returnValue()) {
qCritical() << "Failed to copy wireguard config";
return;
}
} else {
qCritical() << "IPC client not initialized";
return;
}
m_configFileName = "/etc/wireguard/wg99.conf";
#else
m_configFileName = m_configFile.fileName();
#endif
m_isConfigLoaded = true;
@@ -77,9 +152,15 @@ QString WireguardProtocol::configPath() const
return m_configFileName;
}
QString WireguardProtocol::serviceName() const
void WireguardProtocol::updateRouteGateway(QString line)
{
return "AmneziaVPN.WireGuard0";
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
}
ErrorCode WireguardProtocol::start()
@@ -89,6 +170,112 @@ ErrorCode WireguardProtocol::start()
return lastError();
}
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
return startMzImpl();
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
setLastError(ErrorCode::ExecutableMissing);
return lastError();
}
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardConfigExists(configPath());
if (result.returnValue()) {
setLastError(ErrorCode::ConfigMissing);
return lastError();
}
} else {
qCritical() << "IPC client not initialized";
setLastError(ErrorCode::InternalError);
return lastError();
}
setConnectionState(Vpn::ConnectionState::Connecting);
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStartProcess) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->waitForSource(1000);
if (!m_wireguardStartProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStartProcess->setArguments(startArgs());
qDebug() << startArgs().join(" ");
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this,
[this]() { setConnectionState(Vpn::ConnectionState::Connected); });
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAll();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readyRead" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardOutput, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardOutput();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardOutput" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardError, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardError();
reply.waitForFinished(10);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue();
});
m_wireguardStartProcess->start();
m_wireguardStartProcess->waitForFinished(10000);
return ErrorCode::NoError;
}
void WireguardProtocol::updateVpnGateway(const QString &line)
{
}
QString WireguardProtocol::serviceName() const
{
return "AmneziaVPN.WireGuard0";
}
QStringList WireguardProtocol::stopArgs()
{
#ifdef Q_OS_WIN
return { "--remove", configPath() };
#elif defined Q_OS_LINUX
return { "down", "wg99" };
#else
return {};
#endif
}
QStringList WireguardProtocol::startArgs()
{
#ifdef Q_OS_WIN
return { "--add", configPath() };
#elif defined Q_OS_LINUX
return { "up", "wg99" };
#else
return {};
#endif
}
+13
View File
@@ -8,6 +8,7 @@
#include <QTimer>
#include "vpnprotocol.h"
#include "core/ipcclient.h"
#include "mozilla/controllerimpl.h"
@@ -22,21 +23,33 @@ public:
ErrorCode start() override;
void stop() override;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode startMzImpl();
ErrorCode stopMzImpl();
#endif
private:
QString configPath() const;
void writeWireguardConfiguration(const QJsonObject &configuration);
void updateRouteGateway(QString line);
void updateVpnGateway(const QString &line);
QString serviceName() const;
QStringList stopArgs();
QStringList startArgs();
private:
QString m_configFileName;
QFile m_configFile;
QSharedPointer<PrivilegedProcess> m_wireguardStartProcess;
QSharedPointer<PrivilegedProcess> m_wireguardStopProcess;
bool m_isConfigLoaded = false;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
QScopedPointer<ControllerImpl> m_impl;
#endif
};
#endif // WIREGUARDPROTOCOL_H
+4
View File
@@ -233,6 +233,10 @@ QString Settings::routeModeString(RouteMode mode) const
Settings::RouteMode Settings::routeMode() const
{
// TODO implement for mobiles
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
return RouteMode::VpnAllSites;
#endif
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
}
+1 -1
View File
@@ -686,7 +686,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageServiceDnsSettings.qml" line="52"/>
<source>A DNS service is installed on your server, and it is only accessible via VPN.
</source>
<translation>На вашем сервере установлен DNS-сервис, доступ к нему возможен только через VPN.
<translation>На вашем сервере устанавливается DNS-сервис, доступ к нему возможен только через VPN.
</translation>
</message>
<message>
@@ -261,10 +261,6 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
// return QJsonObject();
// }
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(","));
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
QString protocolName = "wireguard";
if (!configMap.value(config_key::junkPacketCount).isEmpty()
&& !configMap.value(config_key::junkPacketMinSize).isEmpty()
+73
View File
@@ -1,5 +1,7 @@
#include "pageController.h"
#include <QProcess>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
@@ -162,3 +164,74 @@ void PageController::closeApplication()
{
qApp->quit();
}
bool PageController::checkForUpdates()
{
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
return false;
#else
QString path = qApp->applicationDirPath();
bool checked = false;
#ifdef Q_OS_MACOS
if(path.contains(qApp->applicationName()+".app/Contents/MacOS")) {
path = path.remove("Contents/MacOS");
}
path = path + "maintenancetool.app";
checked = true;
#endif
#ifdef Q_OS_LINUX
if(path.contains("/client/bin")) {
path = path.remove("/client/bin");
}
path = path + "/maintenancetool";
checked = true;
#endif
if (!checked) {
return false;
}
QStringList argsCheckUpdates;
argsCheckUpdates << "--checkupdates";
QProcess process;
process.start(path, argsCheckUpdates);
// Wait until the update tool is finished
process.waitForFinished();
if (process.error() != QProcess::UnknownError) {
emit showNotificationMessage(tr("Checking for updates: %1").arg(process.errorString()));
return true;
}
// Read the output
QByteArray data = process.readAllStandardOutput();
// No output means no updates available
// Note that the exit code will also be 1, but we don't use that
// Also note that we should parse the output instead of just checking if it is empty if we want specific update info
if (data.isEmpty()) {
emit showNotificationMessage(tr("Checking for updates: %1").arg("it's the latest version"));
return true;
}
// Note: we start it detached because this application need to close for the update
QStringList argsUpdater("--updater");
bool bresult = QProcess::startDetached(path, argsUpdater);
if (!bresult) {
emit showNotificationMessage(tr("Checking for updates: %1").arg("test"));
return true;
}
// Close the application
qApp->quit();
return true;
#endif
}
+2
View File
@@ -90,6 +90,8 @@ public slots:
void closeApplication();
bool checkForUpdates();
signals:
void goToPage(PageLoader::PageEnum page, bool slide = true);
void goToStartPage();
+10 -2
View File
@@ -22,6 +22,10 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
DockerContainer container = ContainerProps::allContainers().at(index.row());
switch (role) {
case NameRole:
// return ContainerProps::containerHumanNames().value(container);
case DescriptionRole:
// return ContainerProps::containerDescriptions().value(container);
case ConfigRole: {
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
@@ -31,15 +35,19 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
break;
}
}
case ServiceTypeRole:
// return ContainerProps::containerService(container);
case DockerContainerRole:
// return container;
case IsInstalledRole:
// return m_settings->containers(m_currentlyProcessedServerIndex).contains(container);
case IsDefaultRole: { //todo remove
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container;
emit defaultContainerChanged();
}
default: break;
}
emit containersModelUpdated();
emit dataChanged(index, index);
return true;
}
-1
View File
@@ -73,7 +73,6 @@ protected:
signals:
void defaultContainerChanged();
void containersModelUpdated();
private:
QMap<DockerContainer, QJsonObject> m_containers;
-6
View File
@@ -193,12 +193,6 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
return primaryDns == protocols::dns::amneziaDnsIp;
}
void ServersModel::updateContainersConfig()
{
auto server = m_settings->server(m_currentlyProcessedServerIndex);
m_servers.replace(m_currentlyProcessedServerIndex, server);
}
QHash<int, QByteArray> ServersModel::roleNames() const
{
QHash<int, QByteArray> roles;
-2
View File
@@ -59,8 +59,6 @@ public slots:
bool isDefaultServerConfigContainsAmneziaDns();
void updateContainersConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
+1 -1
View File
@@ -32,7 +32,7 @@ PageType {
function onRestorePageHomeState(isContainerInstalled) {
buttonContent.state = "expanded"
if (isContainerInstalled) {
containersDropDown.rootButtonClickedFunction()
containersDropDown.menuVisible = true
}
}
function onForceCloseDrawer() {
+7 -1
View File
@@ -192,7 +192,13 @@ PageType {
text: qsTr("Check for updates")
onClicked: {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
PageController.showBusyIndicator(true)
if (!PageController.checkForUpdates()) {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
PageController.showBusyIndicator(false)
}
}
}
@@ -94,7 +94,7 @@ PageType {
DividerType {}
LabelWithButtonType {
visible: true
visible: GC.isDesktop()
Layout.fillWidth: true
@@ -28,11 +28,17 @@ PageType {
ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex())
}
PageController.closePage() // close installing page
PageController.closePage() // close protocol settings page
PageController.goToStartPage()
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) {
PageController.restorePageHomeState(true)
} else if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSettings)) {
PageController.goToPage(PageEnum.PageSettingsServersList, false)
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
if (isServiceInstall) {
PageController.goToPageSettingsServerServices()
}
} else {
PageController.goToPage(PageEnum.PageHome)
}
PageController.showNotificationMessage(finishedMessage)
+1 -23
View File
@@ -68,7 +68,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
}
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns1).toString();
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
@@ -329,8 +329,6 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
return;
}
appendSplitTunnelingConfig();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
if (!m_vpnProtocol) {
@@ -365,26 +363,6 @@ void VpnConnection::createProtocolConnections()
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
}
void VpnConnection::appendSplitTunnelingConfig()
{
auto routeMode = m_settings->routeMode();
auto sites = m_settings->getVpnIps(routeMode);
QJsonArray sitesJsonArray;
for (const auto &site : sites) {
sitesJsonArray.append(site);
}
// Allow traffic to Amezia DNS
if (routeMode == Settings::VpnOnlyForwardSites){
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
}
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
}
#ifdef Q_OS_ANDROID
void VpnConnection::restoreConnection()
{
-2
View File
@@ -112,8 +112,6 @@ private:
#endif
void createProtocolConnections();
void appendSplitTunnelingConfig();
};
#endif // VPNCONNECTION_H
+30 -61
View File
@@ -12,14 +12,13 @@ Usage:
Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) is built.
Options:
-d, --debug Build debug version
-a, --apk (<abi_list> | all) Build APKs for the specified ABIs or for all available ABIs
Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
<abi_list> - list of ABIs delimited by ';'
-b, --build-platform <platform> The SDK platform used for building the Java code of the application
By default, the latest available platform is used
-m, --move Move the build result to the root of the build directory
-h, --help Display this help
-d, --debug Build debug version
-a, --apk <abi> Build APK for the specified ABI
Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
-p, --platform <platform> The SDK platform used for building the Java code of the application
By default, the latest available platform is used
-m, --move Move the build result to the root of the build directory
-h, --help Display this help
EOT
}
@@ -27,25 +26,21 @@ EOT
BUILD_TYPE="release"
AAB=1
opts=$(getopt -l debug,apk:,build-platform:,move,help -o "da:b:mh" -- "$@")
opts=$(getopt -l debug,apk:,platform:,move,help -o "da:p:mh" -- "$@")
eval set -- "$opts"
while true; do
case "$1" in
-d | --debug) BUILD_TYPE="debug"; shift;;
-a | --apk) ABIS=$2; unset AAB; shift 2;;
-b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;;
-a | --apk) ABI=$2; unset AAB; shift 2;;
-p | --platform) ANDROID_PLATFORM=$2; shift 2;;
-m | --move) MOVE_RESULT=1; shift;;
-h | --help) usage; exit 0;;
--) shift; break;;
esac
done
# Validate ABIS parameter
if [[ -v ABIS && \
! "$ABIS" = "all" && \
! "$ABIS" =~ ^((x86|x86_64|armeabi-v7a|arm64-v8a);)*(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then
echo "The 'apk' option must be a list of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a']" \
"delimited by ';' or 'all', but is '$ABIS'"
if [[ -v ABI && ! "$ABI" =~ ^(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then
echo "The 'abi' option must be one of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'], but is '$ABI'"
exit 1
fi
@@ -61,19 +56,13 @@ OUT_APP_DIR=$BUILD_DIR/client
echo "Project dir: $PROJECT_DIR"
echo "Build dir: $BUILD_DIR"
# Determine path to qt bin folder with qt-cmake
if [[ -v AAB || "$ABIS" = "all" ]]; then
if [ -v AAB ]; then
qt_bin_dir_suffix="x86_64"
else
if [[ $ABIS = *";"* ]]; then
oneOf=$(echo $ABIS | cut -d';' -f 1)
else
oneOf=$ABIS
fi
case $oneOf in
case $ABI in
"armeabi-v7a") qt_bin_dir_suffix="armv7";;
"arm64-v8a") qt_bin_dir_suffix="arm64_v8a";;
*) qt_bin_dir_suffix=$oneOf;;
*) qt_bin_dir_suffix=$ABI;;
esac
fi
# get real path
@@ -90,10 +79,10 @@ echo "Using Android NDK in $ANDROID_NDK_ROOT"
# Run qt-cmake to configure build
qt_cmake_opts=()
if [[ -v AAB || "$ABIS" = "all" ]]; then
if [ -v AAB ]; then
qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON)
else
qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABIS")
qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABI")
fi
# QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target
@@ -106,7 +95,7 @@ $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -B $BUILD_DIR \
# Build app
cmake --build $BUILD_DIR --config $BUILD_TYPE
# Build and package APK or AAB
# Build and package APK or AAB. If this is a release, then additionally sign the result.
echo "Building APK/AAB..."
deployqt_opts=()
@@ -115,52 +104,32 @@ if [ -v AAB ]; then
deployqt_opts+=(--aab)
fi
if [ -v ANDROID_BUILD_PLATFORM ]; then
deployqt_opts+=(--android-platform "$ANDROID_BUILD_PLATFORM")
if [ -v ANDROID_PLATFORM ]; then
deployqt_opts+=(--android-platform "$ANDROID_PLATFORM")
fi
if [ "$BUILD_TYPE" = "release" ]; then
deployqt_opts+=(--release)
deployqt_opts+=(--release --sign)
fi
# for gradle to skip all tasks when it is executed by androiddeployqt
# gradle is started later explicitly
export ANDROIDDEPLOYQT_RUN=1
$QT_HOST_PATH/bin/androiddeployqt \
--input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \
--output $OUT_APP_DIR/android-build \
--gradle \
"${deployqt_opts[@]}"
# run gradle
gradle_opts=()
if [ -v AAB ]; then
gradle_opts+=(bundle"${BUILD_TYPE^}")
else
gradle_opts+=(assemble"${BUILD_TYPE^}")
fi
$OUT_APP_DIR/android-build/gradlew \
--project-dir $OUT_APP_DIR/android-build \
-DexplicitRun=1 \
"${gradle_opts[@]}"
if [[ -v CI || -v MOVE_RESULT ]]; then
echo "Moving APK/AAB..."
if [ -v AAB ]; then
mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/AmneziaVPN-$BUILD_TYPE.aab \
$PROJECT_DIR/deploy/build/
mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/android-build-$BUILD_TYPE.aab \
$PROJECT_DIR/deploy/build/AmneziaVPN-$BUILD_TYPE.aab
else
if [ "$ABIS" = "all" ]; then
ABIS="x86;x86_64;armeabi-v7a;arm64-v8a"
if [ "$BUILD_TYPE" = "release" ]; then
build_suffix="release-signed"
else
build_suffix=$BUILD_TYPE
fi
IFS=';' read -r -a abi_array <<< "$ABIS"
for ABI in "${abi_array[@]}"
do
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/AmneziaVPN-$ABI-$BUILD_TYPE.apk \
$PROJECT_DIR/deploy/build/
done
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/android-build-$build_suffix.apk \
$PROJECT_DIR/deploy/build/AmneziaVPN-$ABI-$build_suffix.apk
fi
fi
+9
View File
@@ -42,6 +42,9 @@ if [ -z "${QT_VERSION+x}" ]; then
elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin
fi
QIF_VERSION=4.6
QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin
fi
echo "Using Qt in $QT_BIN_DIR"
@@ -85,4 +88,10 @@ cp -r $PROJECT_DIR/deploy/installer $BUILD_DIR
$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer
# echo "Generating repository..."
$QIF_BIN_DIR/repogen -p $BUILD_DIR/installer/packages $BUILD_DIR/installer/amneziavpn-linux-repository
# echo "Building online installer..."
$CQTDEPLOYER_DIR/binarycreator.sh --online-only -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages $PROJECT_DIR/deploy/AmneziaVPN_Linux_Online_Installer
+13 -2
View File
@@ -163,7 +163,18 @@ if [ "${MAC_CERT_PW+x}" ]; then
fi
fi
echo "Finished, artifact is $DMG_FILENAME"
echo "Finished to generate local installer, artifact is $DMG_FILENAME"
# restore keychain
security default-keychain -s login.keychain
echo "Generate online installer and repository..."
cd $PROJECT_DIR
DIRNAME=$0
if [ "${DIRNAME:0:1}" = "/" ];then
CURDIR=`dirname $DIRNAME`
else
CURDIR="`pwd`"/"`dirname $DIRNAME`"
fi
source $CURDIR/build_macos_online_installer.sh
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
echo "___________________________________________________________________"
echo "..................repository and online installer.................."
echo "___________________________________________________________________"
set -o errexit -o nounset
while getopts n flag
do
case "${flag}" in
n) NOTARIZE_APP=1;;
esac
done
# Hold on to current directory
PROJECT_DIR=$(pwd)
DEPLOY_DIR=$PROJECT_DIR/deploy
mkdir -p $DEPLOY_DIR/build
BUILD_DIR=$DEPLOY_DIR/build
echo "Project dir: ${PROJECT_DIR}"
echo "Build dir: ${BUILD_DIR}"
APP_NAME=amneziavpn-online-installer
APP_FILENAME=$APP_NAME.app
APP_DOMAIN=org.amneziavpn.package
PLIST_NAME=$APP_NAME.plist
REPO_NAME=amneziavpn-macos-repository
OUT_APP_DIR=$BUILD_DIR/client
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos
DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos
INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data
INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME
DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg
# Search Qt
if [ -z "${QT_VERSION+x}" ]; then
QT_VERSION=6.5.1;
QIF_VERSION=4.6
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/macos/bin
QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin
fi
echo "Using Qt in $QT_BIN_DIR"
echo "Using QIF in $QIF_BIN_DIR"
echo "Generating repository..."
$QIF_BIN_DIR/repogen -p $BUILD_DIR/installer/packages $BUILD_DIR/installer/$REPO_NAME
echo "Building online installer..."
$QIF_BIN_DIR/binarycreator --online-only -c $BUILD_DIR/installer/config/macos.xml -p $BUILD_DIR/installer/packages $INSTALLER_BUNDLE_DIR
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing installer bundle..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $INSTALLER_BUNDLE_DIR
/usr/bin/codesign --verify -vvvv $INSTALLER_BUNDLE_DIR || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing installer bundle..."
/usr/bin/ditto -c -k --keepParent $INSTALLER_BUNDLE_DIR $PROJECT_DIR/Installer_bundle_to_notarize.zip
xcrun notarytool submit $PROJECT_DIR/Installer_bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
rm $PROJECT_DIR/Installer_bundle_to_notarize.zip
sleep 300
xcrun stapler staple $INSTALLER_BUNDLE_DIR
xcrun stapler validate $INSTALLER_BUNDLE_DIR
spctl -a -vvvv $INSTALLER_BUNDLE_DIR || true
fi
fi
echo "Building DMG installer..."
hdiutil create -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing DMG installer..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $DMG_FILENAME
/usr/bin/codesign --verify -vvvv $DMG_FILENAME || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing DMG installer..."
xcrun notarytool submit $DMG_FILENAME --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
sleep 300
xcrun stapler staple $DMG_FILENAME
xcrun stapler validate $DMG_FILENAME
fi
fi
echo "Finished to generate online instaler and repository, artifact is $DMG_FILENAME"
# restore keychain
security default-keychain -s login.keychain
+4 -4
View File
@@ -13,14 +13,14 @@
<AllowSpaceInPath>true</AllowSpaceInPath>
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
<ControlScript>controlscript.js</ControlScript>
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
<RepositorySettingsPageVisible>true</RepositorySettingsPageVisible>
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
<SupportsModify>false</SupportsModify>
<SupportsModify>true</SupportsModify>
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
<RemoteRepositories>
<Repository>
<Url>https://amneziavpn.org/updates/linux</Url>
<Enabled>true</Enabled>
<Url>https://updates.amzsvc.com/beta/linux</Url>
<Enabled>1</Enabled>
<DisplayName>AmneziaVPN - repository for Linux</DisplayName>
</Repository>
</RemoteRepositories>
+4 -4
View File
@@ -13,14 +13,14 @@
<AllowSpaceInPath>true</AllowSpaceInPath>
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
<ControlScript>controlscript.js</ControlScript>
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
<RepositorySettingsPageVisible>true</RepositorySettingsPageVisible>
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
<SupportsModify>false</SupportsModify>
<SupportsModify>true</SupportsModify>
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
<RemoteRepositories>
<Repository>
<Url>https://amneziavpn.org/updates/macos</Url>
<Enabled>true</Enabled>
<Url>https://updates.amzsvc.com/beta/macos</Url>
<Enabled>1</Enabled>
<DisplayName>AmneziaVPN - repository for macOS</DisplayName>
</Repository>
</RemoteRepositories>