Compare commits

...

158 Commits

Author SHA1 Message Date
vladimir.kuznetsov
e89738f57a debug-output 2024-01-15 23:31:21 +07:00
pokamest
2802b42747 Merge pull request #491 from amnezia-vpn/bugfix/current-processed-server-selection
fixed current processed server selection after import/install new server
2024-01-15 05:43:34 -08:00
pokamest
66c5d2f0a8 Updated submodule qtkeychain 2024-01-15 10:06:20 +00:00
pokamest
6dbf4ac62c Release 4.2.0.1 2024-01-15 09:28:48 +00:00
pokamest
ae2872830b Updated submodule qtkeychain 2024-01-15 09:20:13 +00:00
pokamest
6f4a3587e4 Merge pull request #495 from amnezia-vpn/feature/update-awg-core
Update AWG core (v0.1.8)
2024-01-14 17:33:10 -05:00
Mykola Baibuz
145f51906e Update AWG core (v0.1.8) 2024-01-14 21:58:53 +02:00
pokamest
12e72bc74b Merge pull request #481 from amnezia-vpn/refactoring/android
Refactor Android open file method
2024-01-13 06:46:25 -05:00
vladimir.kuznetsov
88cd5825d3 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2024-01-11 20:26:06 +07:00
pokamest
ecdad2a315 Merge branch 'dev' into refactoring/android 2024-01-11 13:01:03 +00:00
pokamest
0398ddd6a2 Merge pull request #488 from amnezia-vpn/bugfix/split-tunnel-reset-on-app-reset
bugfix/split-tunnel-reset-on-app-reset
2024-01-11 07:58:04 -05:00
pokamest
50ea4d3b0f Merge pull request #453 from amnezia-vpn/bugfix/windows-crush-on-utf8-symbolos
added conversion using the system locale
2024-01-11 07:51:22 -05:00
pokamest
77be8169f2 Merge pull request #493 from amnezia-vpn/bugfix/secureqsettings-deadlock-2
now value and setValue of secureQSettings are always called in the main thread
2024-01-11 07:48:42 -05:00
lunardunno
7a435f76b6 ArchLinux_support (#463)
Arch linux support (#464)
2024-01-10 20:15:14 +00:00
pokamest
42949e0dea Merge pull request #492 from amnezia-vpn/bugfix/append-client
fixed insert rows count in appendClient function
2024-01-10 12:42:07 -05:00
pokamest
26db423232 Merge pull request #489 from amnezia-vpn/bugfix/remote-links-on-site
added the ability to change the site link via a translation file
2024-01-10 12:40:19 -05:00
vladimir.kuznetsov
645cf52803 now value and setValue of secureQSettings are always called in the main thread 2024-01-10 23:05:22 +07:00
vladimir.kuznetsov
673b8ad5b2 fixed insert rows count in appendClient function 2024-01-10 11:55:32 +07:00
vladimir.kuznetsov
2a03834bb2 fixed current processed server selection after import/install new server 2024-01-09 00:25:18 +07:00
lunardunno
c1b6149e49 Improve servercontroller.cpp for Ubuntu (#482)
Improve servercontroller.cpp for Ubuntu, CentOS, snd Fedora
2024-01-06 16:41:06 +00:00
vladimir.kuznetsov
0690d86e52 added the ability to change the site link via a translation file 2024-01-06 20:42:43 +07:00
vladimir.kuznetsov
bb8a11d110 added reset routeMode when resetting application settings 2024-01-06 20:38:41 +07:00
pokamest
4f7ba4c9a8 Merge pull request #483 from amnezia-vpn/persian_translation
full persian translation
2023-12-30 09:11:08 -05:00
Morteza Sherafati
aa41e4d915 full persian translation 2023-12-30 11:12:54 +00:00
pokamest
bb43b5451f Merge pull request #478 from amnezia-vpn/bugfix/macos-dns-resolve
Fix resolv-update script for MacOS (OpenVPN DNS)
2023-12-28 14:52:28 -05:00
vladimir.kuznetsov
69dd415ab5 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into bugfix/windows-crush-on-utf8-symbolos 2023-12-28 14:47:19 +07:00
albexk
e605f549bd Merge branch 'dev' into refactoring/android 2023-12-26 18:23:53 +03:00
albexk
a961932b2e Refactor AndroidUtils 2023-12-26 17:41:49 +03:00
albexk
e8cc80f046 Refactor Android open file method
Fix some bugs with mimetype filters when the Qt mimetype database does not match the Android database
2023-12-26 16:30:33 +03:00
vladimir.kuznetsov
67f29ac483 added conversion using the system locale 2023-12-26 13:36:21 +07:00
pokamest
c9bde5cdc0 Merge pull request #479 from amnezia-vpn/feature/client-management-migrations
added migration from version 3 of client management to version 4
2023-12-25 22:00:13 -05:00
pokamest
e878911819 Merge pull request #472 from amnezia-vpn/ksznak
Update amneziavpn_ru.ts
2023-12-25 13:36:03 -05:00
pokamest
f1a0b7f0ef Merge pull request #473 from amnezia-vpn/bugfix/new-client-on-connection
fixed hang after creating configuration on connection
2023-12-25 12:21:44 -05:00
KsZnak
3e0a5104e7 Update amneziavpn_ru.ts 2023-12-25 19:18:23 +02:00
vladimir.kuznetsov
8f53d563a4 moved the client table for cloak and ss to the openvpn folder 2023-12-25 22:49:24 +07:00
pokamest
414740ffb7 Merge pull request #467 from amnezia-vpn/feature/full-access-last-config
full access config no longer contains the last_config field
2023-12-25 10:29:16 -05:00
albexk
7437d47d92 Remove unnecessary permission RECEIVE_BOOT_COMPLETED 2023-12-25 15:16:22 +03:00
vladimir.kuznetsov
a68f19d72f added migration from version 3 of client management to version 4 2023-12-24 21:13:09 +07:00
Mykola Baibuz
8d3e21d46a Fix resolv-update script for MacOS (OpenVPN DNS)
script from https://github.com/andrewgdotcom/openvpn-mac-dns
2023-12-23 23:14:02 +02:00
pokamest
5ad54bfdc1 Merge pull request #452 from amnezia-vpn/refactoring/android
Android refactoring
2023-12-23 13:48:26 -05:00
pokamest
12fbc7d258 Merge pull request #474 from amnezia-vpn/bugfix/ss-in-cloak-container
returned shadowsocks to cloak container
2023-12-22 15:04:21 +00:00
albexk
164b7e2551 Merge branch 'dev' into refactoring/android 2023-12-22 15:38:29 +03:00
albexk
eafac491d8 Add a stub for errors coming from Android
These errors are related to VPN connection errors
2023-12-22 15:35:24 +03:00
pokamest
3cfb6e968d Merge pull request #475 from amnezia-vpn/bugfix/default-server-full-access-sharing
added selection of a default server for full access sharing
2023-12-22 12:21:59 +00:00
albexk
375825125f Fix cloak config 2023-12-22 15:11:48 +03:00
vladimir.kuznetsov
a8520e7545 added selection of a default server for full access sharing 2023-12-22 13:43:46 +07:00
vladimir.kuznetsov
ac154cdd83 returned shadowsocks to cloak container 2023-12-22 13:08:53 +07:00
vladimir.kuznetsov
9290775ab5 added removal of last_config when revoke admin config on client management panel 2023-12-21 23:34:27 +07:00
pokamest
d14e8cdee4 Merge pull request #460 from amnezia-vpn/bugfix/minor-ui-fixes
added an empty string check for the server name and user name change fields
2023-12-21 15:55:33 +00:00
vladimir.kuznetsov
7aac9f9d0e fixed hang after creating configuration on connection
- config created on connection is displayed as admin (platform name) on the client management page
- added config creation time on the client management page
2023-12-21 17:47:34 +07:00
KsZnak
41aaac7d32 Update amneziavpn_ru.ts 2023-12-20 20:16:35 +02:00
albexk
c8d2399db9 Merge branch 'dev' into refactoring/android 2023-12-20 20:47:19 +03:00
KsZnak
f37c8e5fd4 Update amneziavpn_ru.ts 2023-12-20 19:14:15 +02:00
pokamest
9f5025c10b Merge pull request #471 from amnezia-vpn/bugfix/macos_deploy_fix
build_macos.sh fix
2023-12-20 17:13:37 +00:00
pokamest
c27d999c41 build_macos.sh fix 2023-12-20 08:39:43 -08:00
pokamest
9681bea237 Update ts files 2023-12-20 12:14:06 +00:00
pokamest
8905d6352c Merge pull request #465 from amnezia-vpn/persian_translation
added persian translation file and menu item
2023-12-20 12:07:51 +00:00
vladimir.kuznetsov
8599b20678 full access config no longer contains the last_config field 2023-12-18 13:17:39 +07:00
Morteza Sherafati
491f09a51b added persian translation file and menu item 2023-12-17 18:37:07 +00:00
pokamest
0f1519a21f Merge pull request #462 from amnezia-vpn/bugfix/service-containers-search
fixed server search for sftp, dns and tor containers
2023-12-17 16:53:52 +00:00
vladimir.kuznetsov
b892156092 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-16 09:23:44 +07:00
vladimir.kuznetsov
7b1b8dc749 fixed server search for sftp, dns and tor containers 2023-12-15 13:58:30 +07:00
albexk
6d0167dcf3 Get back the ability to open configuration files
Doesn't work in all applications because some applications pass an abstract URI instead of the file path in the 'content' scheme
2023-12-14 22:10:22 +03:00
albexk
0c2d661e1c Disable ShadowSocks support on Android 2023-12-14 21:08:45 +03:00
albexk
95d1440d6f Some minor refactoring 2023-12-14 18:01:55 +03:00
albexk
0cabf60dc4 Fixes and refactoring in file saving and import functions 2023-12-14 18:00:58 +03:00
albexk
836ca1cc6b Switch to Qt fileprovider introduced in 6.6 2023-12-14 17:59:03 +03:00
pokamest
be799daafe Merge pull request #431 from amnezia-vpn/re_second_adaptation_to_diff_os
Improve install_docker.sh
2023-12-14 05:27:29 -08:00
pokamest
ad9d674a03 Merge pull request #368 from amnezia-vpn/feature/import-config-from-cloud
API support
2023-12-14 04:15:38 -08:00
vladimir.kuznetsov
f52c3c430f added notification after config revokation 2023-12-14 11:03:27 +07:00
vladimir.kuznetsov
a91ab0e910 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-13 14:21:51 +07:00
vladimir.kuznetsov
39d1f1677f fixed description update, after changing the default protocol 2023-12-13 14:14:37 +07:00
albexk
b0dcae3586 Disable global split tunneling if a non-default route exists in the Wireguard configuration 2023-12-12 22:48:18 +03:00
vladimir.kuznetsov
ed6351f8f1 added an empty string check for the server name and user name change fields 2023-12-12 17:15:10 +03:00
pokamest
e486a2fb26 Merge pull request #443 from amnezia-vpn/feature/cancel-installation-button
added a button to cancel installation if the package manager on the server is busy
2023-12-12 05:17:40 -08:00
albexk
195bdb947e Refactor import config
Remove the path filter, as the content path may not contain a filename.
Disable import when viewing files.
Config can be imported from:
- shared file
- shared text
- vpn:// link
2023-12-11 22:56:01 +03:00
albexk
1576aed1ea Add network state listening and reconnection
Vpn reconnects when the default network is changed
2023-12-11 15:16:50 +03:00
vladimir.kuznetsov
e66fbc3289 added default container installation, after downloading the config from the api 2023-12-11 13:42:42 +07:00
vladimir.kuznetsov
b4c89ad58f Reworked the interaction between models. Now only serversModel directly interacts with server config 2023-12-08 13:50:03 +07:00
albexk
8cc5846808 Fix application hangs in disconnection state 2023-12-07 22:43:33 +03:00
albexk
2eaaf01ca1 Up AGP to version 8.2.0 2023-12-07 19:28:41 +03:00
albexk
67694c0f96 Fix abort error: 'Pure virtual function called!' 2023-12-06 18:25:11 +03:00
albexk
163e5b2c52 Fix for Qt Creator 2023-12-06 18:25:11 +03:00
albexk
508f1d3a42 Add service bind timeout 2023-12-06 18:24:52 +03:00
albexk
d2207a5255 Up Gradle to version 8.5 2023-12-06 13:38:36 +03:00
albexk
bf03f5c9ae Fix for Qt Creator 2023-12-05 23:53:49 +03:00
albexk
656223f57d Fix rebinding to the service 2023-12-05 16:57:53 +03:00
albexk
dc6e3ec53b Add passing the VPN connection status when rebinding to the service 2023-12-05 16:10:29 +03:00
albexk
5835a756ce Add onError callback to handle errors in protocol threads 2023-12-05 13:56:01 +03:00
vladimir.kuznetsov
f3f98a50ed fixed include path of servercontroller 2023-12-05 17:49:11 +07:00
vladimir.kuznetsov
0f4bb78712 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2023-12-05 17:38:53 +07:00
vladimir.kuznetsov
c4014518cb renamed CloudController to ApiController 2023-12-05 17:38:38 +07:00
vladimir.kuznetsov
3605f62feb added support for the updated api 2023-12-05 17:28:14 +07:00
albexk
5c3e253067 Fix service description in AndroidManifest.xml 2023-12-05 13:09:28 +03:00
albexk
8d43cee52e Remove shadowsocks code 2023-12-04 18:45:53 +03:00
albexk
1e64413904 Fix disconnect bug 2023-12-04 18:23:08 +03:00
pokamest
6fd1ea26ee Merge pull request #438 from amnezia-vpn/bugfix/app-is-running-win11
fixed appProcessIsRunning() for win11
2023-12-01 03:24:49 -08:00
vladimir.kuznetsov
e619fd4af9 replaced loader with PageSetupWizardInstalling when updating container settings 2023-12-01 14:16:27 +07:00
albexk
e7658f9859 Add split tunneling 2023-12-01 00:12:50 +03:00
vladimir.kuznetsov
3defb09da9 added a button to cancel installation if the package manager on the server is busy 2023-11-30 19:21:57 +07:00
vladimir.kuznetsov
a672434909 changed tasklist | findstr на tasklist 2023-11-30 07:13:43 +03:00
pokamest
dd233f77fc Merge pull request #432 from amnezia-vpn/feature/client-management
added client management
2023-11-29 07:04:56 -08:00
vladimir.kuznetsov
02efd9c217 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/client-management 2023-11-29 17:26:26 +03:00
albexk
20f3c0388a Add sending statistics 2023-11-29 17:08:53 +03:00
albexk
ef530780bd Fixes in AmneziaVpnService 2023-11-29 17:01:21 +03:00
albexk
d7ec611ff4 Fix bugs in awg and wireguard protocols 2023-11-29 16:55:17 +03:00
pokamest
b897e7102e Merge pull request #437 from amnezia-vpn/bugfix/minor-ui-fixes
Bugfix/minor UI fixes
2023-11-29 05:34:30 -08:00
pokamest
1cc5c5384e Merge pull request #407 from amnezia-vpn/feature/ss-and-cloak-native-configs
added native config generation for ss and cloak
2023-11-29 05:28:33 -08:00
vladimir.kuznetsov
db602ac65b fixed ss string generation 2023-11-29 10:57:47 +07:00
albexk
eaa209bc3a Add OpenVpn over Cloak module 2023-11-28 22:27:00 +03:00
albexk
51d4aea9e2 Add OpenVpn module 2023-11-28 20:07:39 +03:00
albexk
9738ada946 ProtocolApi refactoring, move network classes to NetworkUtils.kt 2023-11-28 19:47:22 +03:00
albexk
8ec105bee0 Move Log class to org.amnezia.vpn.util package 2023-11-28 19:07:32 +03:00
tiaga
4fd0852bb3 Improve install_docker.sh
- add delays before and after Docker activation
- cancel installation if `sudo` wasn't found
2023-11-27 21:47:54 +07:00
vladimir.kuznetsov
a53e904f7b fixed appProcessIsRunning() for win11 2023-11-27 13:56:52 +07:00
vladimir.kuznetsov
2d22b52b5d limited client name length 2023-11-27 13:42:08 +07:00
vladimir.kuznetsov
426ac49f6f fixed "auto-connect" option 2023-11-27 12:46:59 +07:00
vladimir.kuznetsov
c164814abd fixed default server setting after importing 2023-11-27 10:59:48 +07:00
vladimir.kuznetsov
3084892ed8 added selection of default server and container on the sharing page 2023-11-26 21:15:58 +07:00
albexk
9297f877c4 Add AWG module 2023-11-26 13:07:31 +03:00
vladimir.kuznetsov
1bf808c9ee fixed qr code generation for native configs 2023-11-25 13:02:02 +07:00
albexk
91f44fb394 Up Qt to version 6.6.1 2023-11-24 22:22:15 +03:00
albexk
385a52f676 Vpn service refactoring 2023-11-24 21:51:09 +03:00
albexk
8ef16781eb Remove old wireguard code 2023-11-24 21:49:54 +03:00
albexk
ad5ea1ca44 Add IpcMessenger class 2023-11-24 17:10:08 +03:00
albexk
0ba5d754d5 Android Activity refactoring 2023-11-23 21:20:31 +03:00
albexk
ccdcfdce8a Remove VPNPermissionHelper class 2023-11-23 16:49:32 +03:00
albexk
712fb4d0d3 Add Wireguard module 2023-11-23 16:45:15 +03:00
albexk
de65a03998 Add ProtocolApi module 2023-11-23 16:03:52 +03:00
albexk
6d6710db4a Prefs refactoring 2023-11-23 14:19:51 +03:00
albexk
c34c3e51ea Remove 'https://jitpack.io' repository 2023-11-23 14:13:38 +03:00
vladimir.kuznetsov
5dc3b64e0b added search bar for client management page 2023-11-23 14:54:49 +07:00
vladimir.kuznetsov
e8ceeb6e20 added full access sharing 2023-11-23 00:04:06 +07:00
pokamest
d38c7ce6a5 Error codes cleanup 2023-11-22 13:57:05 +00:00
albexk
e625543b94 CameraActivity refactoring 2023-11-21 23:35:26 +03:00
albexk
679bd4e4c9 Get rid of AppCompat theme 2023-11-21 21:31:49 +03:00
vladimir.kuznetsov
c6a312845a added client management 2023-11-21 20:31:53 +07:00
pokamest
ef0530ec6b Merge pull request #420 from amnezia-vpn/adaptation_to_different_os
Improve logic of install_docker.sh
2023-11-17 11:51:58 -08:00
albexk
2c98a90d60 Move icons to mipmap folders 2023-11-17 15:30:13 +03:00
albexk
b90fad6664 Android activity and AndroidController class refactoring 2023-11-17 15:10:11 +03:00
albexk
5a5ea4a018 Android project restructuring 2023-11-16 20:16:28 +03:00
albexk
bc68c487ee Update to qt 6.6.0 2023-11-16 19:49:36 +03:00
albexk
617cdf14ad Log class refactoring 2023-11-16 15:30:44 +03:00
albexk
f1c970461f Create utils module, move Log class there
BuildConfig class is now only created in the utils module
2023-11-16 15:15:02 +03:00
albexk
2fde47a86f Move qt binding java code to a separate module 2023-11-15 22:10:44 +03:00
albexk
4e5f2f44b6 Detach shadowsocks module 2023-11-15 22:08:00 +03:00
pokamest
e8a2e54d05 Typo fix 2023-11-15 12:51:39 +00:00
tiaga
362a82f944 Improve logic of install_docker.sh
- check packages update only when it's required
- avoid `dnf/yum update` for RHEL-based systems
2023-11-14 16:57:16 +07:00
vladimir.kuznetsov
aee82282ac Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/import-config-from-cloud 2023-11-06 14:10:43 +03:00
vladimir.kuznetsov
8497aeeb91 removed the debugging code 2023-11-06 14:10:31 +03:00
pokamest
147726ecb0 Merge branch 'dev' into feature/import-config-from-cloud 2023-11-01 21:42:07 +00:00
vladimir.kuznetsov
9cfcb714ae added native config generation for ss and cloak 2023-11-01 21:29:58 +05:00
pokamest
40725d4155 Merge pull request #397 from amnezia-vpn/dev
Release 4.0.8
2023-10-26 11:45:11 -07:00
vladimir.kuznetsov
3e03002ead added getting cloud config for cloak 2023-10-23 23:55:01 +05:00
vladimir.kuznetsov
4ae9cddcce fixed a typo in the serverController include 2023-10-23 23:53:46 +05:00
vladimir.kuznetsov
16724645ce Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/import-config-from-cloud 2023-10-23 21:40:18 +05:00
vladimir.kuznetsov
25f8283edd added getting the config from the cloud service 2023-10-12 20:48:03 +05:00
pokamest
9090ec54e7 Merge pull request #339 from amnezia-vpn/dev
Release 3.1.0.1
2023-09-21 07:28:08 -07:00
307 changed files with 10563 additions and 85413 deletions

View File

@@ -275,7 +275,7 @@ jobs:
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.5.3
QT_VERSION: 6.6.1
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps:

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.2.0.1
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)

View File

@@ -56,6 +56,7 @@ set(CMAKE_AUTORCC ON)
set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
@@ -107,7 +108,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.h
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
@@ -146,7 +147,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp

View File

@@ -91,13 +91,11 @@ void AmneziaApplication::init()
initControllers();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::initialized, this,
[this](bool status, bool connected, const QDateTime &connectionDate) {
if (connected) {
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
}
connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
[this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qCritical() << QString("Init failed");
@@ -277,19 +275,16 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
void AmneziaApplication::initModels()
{
m_containersModel.reset(new ContainersModel(m_settings, this));
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(),
&ContainersModel::updateContainersConfig);
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
&ServersModel::updateContainersConfig);
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
&ContainersModel::setDefaultContainer);
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@@ -298,7 +293,7 @@ void AmneziaApplication::initModels()
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
@@ -324,6 +319,19 @@ void AmneziaApplication::initModels()
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked,
m_serversModel.get(), &ServersModel::clearCachedProfile);
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials) {
m_serversModel->reloadContainerConfig();
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
emit m_configurator->clientModelUpdated();
});
}
void AmneziaApplication::initControllers()
@@ -349,18 +357,24 @@ void AmneziaApplication::initControllers()
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator));
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel,
m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(),
&ServersModel::toggleAmneziaDns);
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
}

View File

@@ -24,6 +24,7 @@
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/apiController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
@@ -39,6 +40,7 @@
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
@@ -94,6 +96,7 @@ private:
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
@@ -118,6 +121,7 @@ private:
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<ApiController> m_cloudController;
};
#endif // AMNEZIA_APPLICATION_H

View File

@@ -15,28 +15,26 @@
<!-- %%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" />
<!-- To request network state -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<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"/> -->
<application
android:name=".qt.AmneziaApp"
android:name=".AmneziaApplication"
android:label="-- %%INSERT_APP_NAME%% --"
android:allowNativeHeapPointerTagging="false"
android:icon="@drawable/icon"
android:roundIcon="@drawable/icon_round"
android:theme="@style/Theme.AppCompat.NoActionBar">
android:icon="@mipmap/icon"
android:roundIcon="@mipmap/icon_round"
android:theme="@style/NoActionBar">
<activity
android:name=".qt.VPNActivity"
android:name=".AmneziaActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
@@ -48,7 +46,7 @@
</intent-filter>
<intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<action android:name="org.amnezia.vpn.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
@@ -62,57 +60,65 @@
</activity>
<activity
android:name=".qt.CameraActivity"
android:name=".CameraActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false" />
<activity
android:name=".qt.ImportConfigActivity"
android:exported="true">
android:name=".VpnRequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />
<intent-filter android:label="AmneziaVPN">
<activity
android:name=".ImportConfigActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="true"
android:theme="@style/Translucent">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="vpn" android:host="*" />
</intent-filter>
<intent-filter>
<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" />
</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" />
@@ -122,11 +128,11 @@
</activity>
<service
android:name=".VPNService"
android:process=":QtOnlyProcess"
android:name=".AmneziaVpnService"
android:process=":amneziaVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:exported="true">
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService" />
@@ -135,23 +141,12 @@
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service>
<service
android:name=".qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:exported="true">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</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:authorities="org.amnezia.vpn.qtprovider"
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/qtprovider_paths" />
</provider>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
[proxy_all]
[bypass_list]
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.awg"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":wireguard"))
}

View File

@@ -0,0 +1,80 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.json.JSONObject
/**
* Config example:
* {
* "protocol": "awg",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "awg_config_data": {
* "H1": "969537490",
* "H2": "481688153",
* "H3": "2049399200",
* "H4": "52029755",
* "Jc": "3",
* "Jmax": "1000",
* "Jmin": "50",
* "S1": "49",
* "S2": "60",
* "client_ip": "10.8.1.1",
* "hostName": "100.100.100.0",
* "port": 12345,
* "client_pub_key": "clientPublicKeyBase64",
* "client_priv_key": "privateKeyBase64",
* "psk_key": "presharedKeyBase64",
* "server_pub_key": "publicKeyBase64",
* "config": "[Interface]
* Address = 10.8.1.1/32
* DNS = 1.1.1.1, 1.0.0.1
* PrivateKey = privateKeyBase64
* Jc = 3
* Jmin = 50
* Jmax = 1000
* S1 = 49
* S2 = 60
* H1 = 969537490
* H2 = 481688153
* H3 = 2049399200
* H4 = 52029755
*
* [Peer]
* PublicKey = publicKeyBase64
* PresharedKey = presharedKeyBase64
* AllowedIPs = 0.0.0.0/0, ::/0
* Endpoint = 100.100.100.0:12345
* PersistentKeepalive = 25
* "
* }
* }
*/
class Awg : Wireguard() {
override val ifName: String = "awg0"
override fun parseConfig(config: JSONObject): AwgConfig {
val configDataJson = config.getJSONObject("awg_config_data")
val configData = parseConfigData(configDataJson.getString("config"))
return AwgConfig.build {
configWireguard(configData)
configSplitTunneling(config)
configData["Jc"]?.let { setJc(it.toInt()) }
configData["Jmin"]?.let { setJmin(it.toInt()) }
configData["Jmax"]?.let { setJmax(it.toInt()) }
configData["S1"]?.let { setS1(it.toInt()) }
configData["S2"]?.let { setS2(it.toInt()) }
configData["H1"]?.let { setH1(it.toLong()) }
configData["H2"]?.let { setH2(it.toLong()) }
configData["H3"]?.let { setH3(it.toLong()) }
configData["H4"]?.let { setH4(it.toLong()) }
}
}
}

View File

@@ -0,0 +1,108 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
class AwgConfig private constructor(
wireguardConfigBuilder: WireguardConfig.Builder,
val jc: Int,
val jmin: Int,
val jmax: Int,
val s1: Int,
val s2: Int,
val h1: Long,
val h2: Long,
val h3: Long,
val h4: Long
) : WireguardConfig(wireguardConfigBuilder) {
private constructor(builder: Builder) : this(
builder,
builder.jc,
builder.jmin,
builder.jmax,
builder.s1,
builder.s2,
builder.h1,
builder.h2,
builder.h3,
builder.h4
)
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
super.appendDeviceLine(this)
appendLine("jc=$jc")
appendLine("jmin=$jmin")
appendLine("jmax=$jmax")
appendLine("s1=$s1")
appendLine("s2=$s2")
appendLine("h1=$h1")
appendLine("h2=$h2")
appendLine("h3=$h3")
appendLine("h4=$h4")
}
class Builder : WireguardConfig.Builder() {
private var _jc: Int? = null
internal var jc: Int
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
private set(value) { _jc = value }
private var _jmin: Int? = null
internal var jmin: Int
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
private set(value) { _jmin = value }
private var _jmax: Int? = null
internal var jmax: Int
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
private set(value) { _jmax = value }
private var _s1: Int? = null
internal var s1: Int
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
private set(value) { _s1 = value }
private var _s2: Int? = null
internal var s2: Int
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
private set(value) { _s2 = value }
private var _h1: Long? = null
internal var h1: Long
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
private set(value) { _h1 = value }
private var _h2: Long? = null
internal var h2: Long
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
private set(value) { _h2 = value }
private var _h3: Long? = null
internal var h3: Long
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
private set(value) { _h3 = value }
private var _h4: Long? = null
internal var h4: Long
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
private set(value) { _h4 = value }
fun setJc(jc: Int) = apply { this.jc = jc }
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 }
override fun build(): AwgConfig = AwgConfig(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
}
}

View File

@@ -1,3 +1,5 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
@@ -11,13 +13,12 @@ kotlin {
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val qtAndroidDir: String by gradleProperties
val outputBaseName: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
buildConfig = true
viewBinding = true
}
@@ -42,8 +43,9 @@ android {
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("$qtAndroidDir/src", "src"))
res.setSrcDirs(listOf("$qtAndroidDir/res", "res"))
java.setSrcDirs(listOf("src"))
res.setSrcDirs(listOf("res"))
// androyddeployqt creates the folders below
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
@@ -76,17 +78,38 @@ android {
isUniversalApk = false
}
}
// fix for Qt Creator to allow deploying the application to a device
// to enable this fix, add the line outputBaseName=android-build to local.properties
if (outputBaseName.isNotEmpty()) {
applicationVariants.all {
outputs.map { it as BaseVariantOutputImpl }
.forEach { output ->
if (output.outputFileName.endsWith(".apk")) {
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
}
}
}
}
lint {
disable += "InvalidFragmentVersionForActivityResult"
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(project(":qt"))
implementation(project(":utils"))
implementation(project(":protocolApi"))
implementation(project(":wireguard"))
implementation(project(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity)
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)
}

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.cloak"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":openvpn"))
}

View File

@@ -0,0 +1,69 @@
package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "cloak",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* "cloak_config_data": {
* "BrowserSig": "chrome",
* "EncryptionMethod": "aes-gcm",
* "NumConn": 1,
* "ProxyMethod": "openvpn",
* "PublicKey": "PublicKey=",
* "RemoteHost": "100.100.100.0",
* "RemotePort": "443",
* "ServerName": "servername",
* "StreamTimeout": 300,
* "Transport": "direct",
* "UID": "UID="
* }
* }
*/
class Cloak : OpenVpn() {
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
openVpnConfig.usePluggableTransports = true
openVpnConfig.content = configStr
return openVpnConfig
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
cloakConfigJson.put("NumConn", 1)
cloakConfigJson.put("ProxyMethod", "openvpn")
if (cloakConfigJson.has("port")) {
val port = cloakConfigJson["port"]
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", port)
}
if (cloakConfigJson.has("remote")) {
val remote = cloakConfigJson["remote"]
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", remote)
}
return cloakConfigJson
}
}

View File

@@ -1,16 +1,18 @@
[versions]
agp = "8.1.3"
agp = "8.2.0"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-appcompat = "1.6.1"
androidx-camera = "1.2.3"
androidx-activity = "1.8.1"
androidx-annotation = "1.7.0"
androidx-camera = "1.3.0"
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-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
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" }
@@ -18,8 +20,6 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
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 = [

View File

@@ -24,7 +24,7 @@ private class PropertyDelegate(
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)
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name).orEmpty()
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>

Binary file not shown.

View File

@@ -1,5 +1,7 @@
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-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.openvpn"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(libs.kotlinx.coroutines)
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Config {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Config obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ConnectionInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ConnectionInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_DynamicChallenge {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_DynamicChallenge obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_EvalConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_EvalConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Event {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Event obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKICertRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKICertRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIRequestBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIRequestBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKISignRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKISignRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_InterfaceStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_InterfaceStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_KeyValue {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_KeyValue obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_LLVector extends java.util.AbstractList<Long> implements
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LLVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_LogInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LogInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_MergeConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_MergeConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -20,6 +20,18 @@ public class ClientAPI_OpenVPNClient extends ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClient obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_OpenVPNClientHelper {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClientHelper obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ProvideCreds {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ProvideCreds obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_RemoteOverride {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_RemoteOverride obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntry {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntry obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntryVector extends java.util.AbstractList<ClientAP
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntryVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_SessionToken {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_SessionToken obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_Status {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Status obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_StringVec extends java.util.AbstractList<String> implemen
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_StringVec obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_TransportStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TransportStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -21,6 +21,18 @@ public class ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TunBuilderBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__string {
protected static long getCPtr(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t {
protected static long getCPtr(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View File

@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View File

@@ -0,0 +1,136 @@
package org.amnezia.vpn.protocol.openvpn
import android.content.Context
import android.net.VpnService.Builder
import android.os.Build
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.getLocalNetworks
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "openvpn",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* }
*/
open class OpenVpn : Protocol() {
private lateinit var context: Context
private var openVpnClient: OpenVpnClient? = null
private lateinit var scope: CoroutineScope
override val statistics: Statistics
get() {
openVpnClient?.let { client ->
val stats = client.transport_stats()
return Statistics.build {
setRxBytes(stats.bytesIn)
setTxBytes(stats.bytesOut)
}
}
return Statistics.EMPTY_STATISTICS
}
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
super.initialize(context, state, onError)
loadSharedLibrary(context, "ovpn3")
this.context = context
scope = CoroutineScope(Dispatchers.IO)
}
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(
configBuilder = configBuilder,
state = state,
getLocalNetworks = { ipv6 -> getLocalNetworks(context, ipv6) },
establish = makeEstablish(vpnBuilder),
protect = protect,
onError = onError
)
try {
openVpnClient?.let { client ->
val openVpnConfig = parseConfig(config)
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
configBuilder.apply {
// fix for split tunneling
// The exclude split tunneling OpenVpn configuration does not contain a default route.
// It is required for split tunneling in newer versions of Android.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
addRoute(InetNetwork("0.0.0.0", 0))
addRoute(InetNetwork("::", 0))
}
configSplitTunneling(config)
}
scope.launch {
val status = client.connect()
if (status.error) {
state.value = DISCONNECTED
onError("OpenVpn connect() error: ${status.status}: ${status.message}")
}
}
}
} catch (e: Exception) {
openVpnClient = null
throw e
}
}
override fun stopVpn() {
openVpnClient?.stop()
openVpnClient = null
}
override fun reconnectVpn(vpnBuilder: Builder) {
openVpnClient?.let {
it.establish = makeEstablish(vpnBuilder)
it.reconnect(0)
}
}
protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
return openVpnConfig
}
private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder ->
val openVpnConfig = configBuilder.build()
buildVpnInterface(openVpnConfig, vpnBuilder)
vpnBuilder.establish().use { tunFd ->
if (tunFd == null) {
throw VpnStartException("Create VPN interface: permission not granted or revoked")
}
return@use tunFd.detachFd()
}
}
}

View File

@@ -0,0 +1,428 @@
package org.amnezia.vpn.protocol.openvpn
import android.net.ProxyInfo
import android.os.Build
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event
import net.openvpn.ovpn3.ClientAPI_LogInfo
import net.openvpn.ovpn3.ClientAPI_OpenVPNClient
import net.openvpn.ovpn3.ClientAPI_Status
import net.openvpn.ovpn3.ClientAPI_StringVec
import net.openvpn.ovpn3.ClientAPI_TransportStats
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
private const val TAG = "OpenVpnClient"
private const val EMULATED_EXCLUDE_ROUTES = (1 shl 16)
class OpenVpnClient(
private val configBuilder: OpenVpnConfig.Builder,
private val state: MutableStateFlow<ProtocolState>,
private val getLocalNetworks: (Boolean) -> List<InetNetwork>,
internal var establish: (OpenVpnConfig.Builder) -> Int,
private val protect: (Int) -> Boolean,
private val onError: (String) -> Unit
) : ClientAPI_OpenVPNClient() {
/**************************************************************************
* Tun builder callbacks
**************************************************************************/
// Tun builder methods, loosely based on the Android VpnService.Builder
// abstraction. These methods comprise an abstraction layer that
// allows the OpenVPN C++ core to call out to external methods for
// establishing the tunnel, adding routes, etc.
// All methods returning bool use the return
// value to indicate success (true) or fail (false).
// tun_builder_new() should be called first, then arbitrary setter methods,
// and finally tun_builder_establish to return the socket descriptor
// for the session. IP addresses are pre-validated before being passed to
// these methods.
// This interface is based on Android's VpnService.Builder.
// Callback to construct a new tun builder
// Should be called first.
override fun tun_builder_new(): Boolean {
Log.v(TAG, "tun_builder_new")
configBuilder.clearAddresses()
return true
}
// Callback to set MTU of the VPN interface
// Never called more than once per tun_builder session.
override fun tun_builder_set_mtu(mtu: Int): Boolean {
Log.v(TAG, "tun_builder_set_mtu: $mtu")
configBuilder.setMtu(mtu)
return true
}
// Callback to add network address to VPN interface
// May be called more than once per tun_builder session
override fun tun_builder_add_address(
address: String, prefix_length: Int,
gateway: String, ipv6: Boolean, net30: Boolean
): Boolean {
Log.v(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
configBuilder.addAddress(InetNetwork(address, prefix_length))
return true
}
// Callback to add route to VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
if (address == "remote_host") return false
configBuilder.addRoute(InetNetwork(address, prefix_length))
return true
}
// Callback to exclude route from VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
}
return true
}
// Callback to add DNS server to VPN interface
// May be called more than once per tun_builder session
// If reroute_dns is true, all DNS traffic should be routed over the
// tunnel, while if false, only DNS traffic that matches an added search
// domain should be routed.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_dns_server: $address, $ipv6")
configBuilder.addDnsServer(parseInetAddress(address))
return true
}
// Optional callback that indicates whether traffic of a certain
// address family (AF_INET or AF_INET6) should be
// blocked or allowed, to prevent unencrypted packet leakage when
// the tunnel is IPv4-only/IPv6-only, but the local machine
// has connectivity with the other protocol to the internet.
// Controlled by "block-ipv6" and block-ipv6 config var.
// If addresses are added for a family this setting should be
// ignored for that family
// See also Android's VPNService.Builder.allowFamily method
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_allow_family: $af, $allow")
return true
} */
// Callback to set address of remote server
// Never called more than once per tun_builder session.
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_remote_address: $address, $ipv6")
return true
}
// Optional callback that indicates OSI layer, should be 2 or 3.
// Defaults to 3.
override fun tun_builder_set_layer(layer: Int): Boolean {
Log.v(TAG, "tun_builder_set_layer: $layer")
return layer == 3
}
// Callback to set the session name
// Never called more than once per tun_builder session.
override fun tun_builder_set_session_name(name: String): Boolean {
Log.v(TAG, "tun_builder_set_session_name: $name")
return true
}
// Callback to establish the VPN tunnel, returning a file descriptor
// to the tunnel, which the caller will henceforth own. Returns -1
// if the tunnel could not be established.
// Always called last after tun_builder session has been configured.
override fun tun_builder_establish(): Int {
Log.v(TAG, "tun_builder_establish")
return establish(configBuilder)
}
// Callback to reroute default gateway to VPN interface.
// ipv4 is true if the default route to be added should be IPv4.
// ipv6 is true if the default route to be added should be IPv6.
// flags are defined in RGWFlags (rgwflags.hpp).
// Never called more than once per tun_builder session.
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
Log.v(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
if (ipv4) {
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
}
if (ipv6) {
configBuilder.addRoute(InetNetwork("::", 0))
}
return true
}
// Callback to add search domain to DNS resolver
// May be called more than once per tun_builder session
// See tun_builder_add_dns_server above for description of
// reroute_dns parameter.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_search_domain(domain: String): Boolean {
Log.v(TAG, "tun_builder_add_search_domain: $domain")
configBuilder.setSearchDomain(domain)
return true
}
// Callback to set the HTTP proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_http: $host, $port")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
} catch (e: Exception) {
Log.e(TAG, "Could not set proxy: ${e.message}")
return false
}
}
return true
}
// Callback to set the HTTPS proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_https: $host, $port")
return false
}
// When the exclude local network option is enabled this
// function is called to get a list of local networks so routes
// to exclude them from the VPN network are generated
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
Log.v(TAG, "tun_builder_get_local_networks: $ipv6")
val networks = ClientAPI_StringVec()
for (address in getLocalNetworks(ipv6)) {
networks.add(address.toString())
}
return networks
}
// Optional callback to set default value for route metric.
// Guaranteed to be called before other methods that deal
// with routes such as tun_builder_add_route and
// tun_builder_reroute_gw. Route metric is ignored
// if < 0.
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
Log.v(TAG, "tun_builder_set_route_metric_default: $metric")
return super.tun_builder_set_route_metric_default(metric)
} */
// Callback to add a host which should bypass the proxy
// May be called more than once per tun_builder session
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
Log.v(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
return super.tun_builder_add_proxy_bypass(bypass_host)
} */
// Callback to set the proxy "Auto Config URL"
// Never called more than once per tun_builder session.
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
Log.v(TAG, "tun_builder_set_proxy_auto_config_url: $url")
return super.tun_builder_set_proxy_auto_config_url(url)
} */
// Callback to add Windows WINS server to VPN interface.
// WINS server addresses are always IPv4.
// May be called more than once per tun_builder session.
// Guaranteed to be called after tun_builder_reroute_gw.
/* override fun tun_builder_add_wins_server(address: String): Boolean {
Log.v(TAG, "tun_builder_add_wins_server: $address")
return super.tun_builder_add_wins_server(address)
} */
// Optional callback to set a DNS suffix on tun/tap adapter.
// Currently only implemented on Windows, where it will
// set the "Connection-specific DNS Suffix" property on
// the TAP driver.
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
Log.v(TAG, "tun_builder_set_adapter_domain_suffix: $name")
return super.tun_builder_set_adapter_domain_suffix(name)
} */
// Return true if tun interface may be persisted, i.e. rolled
// into a new session with properties untouched. This method
// is only called after all other tests of persistence
// allowability succeed, therefore it can veto persistence.
// If persistence is ultimately enabled,
// tun_builder_establish_lite() will be called. Otherwise,
// tun_builder_establish() will be called.
/* override fun tun_builder_persist(): Boolean {
Log.v(TAG, "tun_builder_persist")
return super.tun_builder_persist()
} */
// Indicates a reconnection with persisted tun state.
/* override fun tun_builder_establish_lite() {
Log.v(TAG, "tun_builder_establish_lite")
super.tun_builder_establish_lite()
} */
// Indicates that tunnel is being torn down.
// If disconnect == true, then the teardown is occurring
// prior to final disconnect.
/* override fun tun_builder_teardown(disconnect: Boolean) {
Log.v(TAG, "tun_builder_teardown: $disconnect")
super.tun_builder_teardown(disconnect)
} */
/**************************************************************************
* Connection control methods
**************************************************************************/
// Parse OpenVPN configuration file.
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
Log.v(TAG, "eval_config")
return super.eval_config(arg0)
}
// Primary VPN client connect method, doesn't return until disconnect.
// Should be called by a worker thread. This method will make callbacks
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
override fun connect(): ClientAPI_Status {
Log.v(TAG, "connect")
return super.connect()
}
// Callback to "protect" a socket from being routed through the tunnel.
// Will be called from the thread executing connect().
// The remote and ipv6 are the remote host this socket will connect to
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
Log.v(TAG, "socket_protect: $socket, $remote, $ipv6")
return protect(socket)
}
// Stop the client. Only meaningful when connect() is running.
// May be called asynchronously from a different thread
// when connect() is running.
override fun stop() {
Log.v(TAG, "stop")
super.stop()
}
// Pause the client -- useful to avoid continuous reconnection attempts
// when network is down. May be called from a different thread
// when connect() is running.
override fun pause(reason: String) {
Log.v(TAG, "pause: $reason")
super.pause(reason)
}
// Resume the client after it has been paused. May be called from a
// different thread when connect() is running.
override fun resume() {
Log.v(TAG, "resume")
super.resume()
}
// Do a disconnect/reconnect cycle n seconds from now. May be called
// from a different thread when connect() is running.
override fun reconnect(seconds: Int) {
Log.v(TAG, "reconnect")
super.reconnect(seconds)
}
// When a connection is close to timeout, the core will call this
// method. If it returns false, the core will disconnect with a
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
// state.
override fun pause_on_connection_timeout(): Boolean {
Log.v(TAG, "pause_on_connection_timeout")
return false
}
// Return information about the most recent connection. Should be called
// after an event of type "CONNECTED".
/* override fun connection_info(): ClientAPI_ConnectionInfo {
Log.v(TAG, "connection_info")
return super.connection_info()
} */
/**************************************************************************
* Status callbacks
**************************************************************************/
// Callback for delivering events during connect() call.
// Will be called from the thread executing connect().
override fun event(event: ClientAPI_Event) {
val name = event.name
val info = event.info
Log.v(TAG, "OpenVpn event: $name: $info")
when (name) {
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
"CONNECTED" -> state.value = CONNECTED
"DISCONNECTED" -> state.value = DISCONNECTED
"RECONNECTING" -> {
state.getAndUpdate { state ->
if (state == DISCONNECTED || state == CONNECTED) RECONNECTING
else state
}
}
}
if (event.error || event.fatal) {
state.value = DISCONNECTED
onError("OpenVpn ${if (event.error) "ERROR" else "FATAL"}: $name: $info")
}
}
// Callback for logging.
// Will be called from the thread executing connect().
override fun log(arg0: ClientAPI_LogInfo) {
arg0.text.dropLastWhile { it == '\n' }.let {
Log.d(TAG, "OpenVpnLog: $it")
}
}
/**************************************************************************
* Stats methods
**************************************************************************/
// return transport stats only
override fun transport_stats(): ClientAPI_TransportStats {
Log.v(TAG, "transport_stats")
return super.transport_stats()
}
// return a stats value, index should be >= 0 and < stats_n()
/* override fun stats_value(index: Int): Long {
Log.v(TAG, "stats_value: $index")
return super.stats_value(index)
} */
// return all stats in a bundle
/* override fun stats_bundle(): ClientAPI_LLVector {
Log.v(TAG, "stats_bundle")
return super.stats_bundle()
} */
// return tun stats only
/* override fun tun_stats(): ClientAPI_InterfaceStats {
Log.v(TAG, "tun_stats")
return super.tun_stats()
} */
// post control channel message
/* override fun post_cc_msg(msg: String) {
Log.v(TAG, "post_cc_msg: $msg")
super.post_cc_msg(msg)
} */
}

View File

@@ -0,0 +1,20 @@
package org.amnezia.vpn.protocol.openvpn
import org.amnezia.vpn.protocol.ProtocolConfig
private const val OPENVPN_DEFAULT_MTU = 1500
class OpenVpnConfig private constructor(
protocolConfigBuilder: ProtocolConfig.Builder
) : ProtocolConfig(protocolConfigBuilder) {
class Builder : ProtocolConfig.Builder(false) {
override var mtu: Int = OPENVPN_DEFAULT_MTU
override fun build(): OpenVpnConfig = OpenVpnConfig(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): OpenVpnConfig = Builder().apply(block).build()
}
}

View File

@@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol"
}
dependencies {
compileOnly(project(":utils"))
implementation(libs.androidx.annotation)
implementation(libs.kotlinx.coroutines)
}

View File

@@ -0,0 +1,9 @@
package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)

View File

@@ -0,0 +1,217 @@
package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context
import android.net.IpPrefix
import android.net.VpnService
import android.net.VpnService.Builder
import android.os.Build
import android.system.OsConstants
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.IpRange
import org.amnezia.vpn.util.net.IpRangeSet
import org.json.JSONObject
private const val TAG = "Protocol"
const val VPN_SESSION_NAME = "AmneziaVPN"
private const val SPLIT_TUNNEL_DISABLE = 0
private const val SPLIT_TUNNEL_INCLUDE = 1
private const val SPLIT_TUNNEL_EXCLUDE = 2
abstract class Protocol {
abstract val statistics: Statistics
protected lateinit var state: MutableStateFlow<ProtocolState>
protected lateinit var onError: (String) -> Unit
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
this.state = state
this.onError = onError
}
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()
abstract fun reconnectVpn(vpnBuilder: Builder)
protected fun ProtocolConfig.Builder.configSplitTunneling(config: JSONObject) {
if (!allowSplitTunneling) {
Log.i(TAG, "Global address split tunneling is prohibited, " +
"only tunneling from the protocol config is used")
return
}
val splitTunnelType = config.optInt("splitTunnelType")
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
val splitTunnelSites = config.getJSONArray("splitTunnelSites")
when (splitTunnelType) {
SPLIT_TUNNEL_INCLUDE -> {
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
// add routes from config
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
addRoute(address)
}
}
SPLIT_TUNNEL_EXCLUDE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// exclude routes from config
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
excludeRoute(address)
}
} else {
// For older versions of Android, build a list of subnets without excluded addresses
val ipRangeSet = IpRangeSet()
ipRangeSet.remove(IpRange("127.0.0.0", 8))
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
ipRangeSet.remove(IpRange(address))
}
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
ipRangeSet.subnets().forEach(::addRoute)
addRoute(InetNetwork("2000::", 3))
}
}
}
}
protected open fun buildVpnInterface(config: ProtocolConfig, vpnBuilder: Builder) {
vpnBuilder.setSession(VPN_SESSION_NAME)
for (addr in config.addresses) {
Log.d(TAG, "addAddress: $addr")
vpnBuilder.addAddress(addr)
}
for (addr in config.dnsServers) {
Log.d(TAG, "addDnsServer: $addr")
vpnBuilder.addDnsServer(addr)
}
// fix for Samsung android ignoring DNS servers outside the VPN route range
if (Build.BRAND == "samsung") {
for (addr in config.dnsServers) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(InetNetwork(addr))
}
}
config.searchDomain?.let {
Log.d(TAG, "addSearchDomain: $it")
vpnBuilder.addSearchDomain(it)
}
for (addr in config.routes) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(addr)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
for (addr in config.excludedRoutes) {
Log.d(TAG, "excludeRoute: $addr")
vpnBuilder.excludeRoute(addr)
}
}
for (app in config.excludedApplications) {
Log.d(TAG, "addDisallowedApplication: $app")
vpnBuilder.addDisallowedApplication(app)
}
Log.d(TAG, "setMtu: ${config.mtu}")
vpnBuilder.setMtu(config.mtu)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
config.httpProxy?.let {
Log.d(TAG, "setHttpProxy: $it")
vpnBuilder.setHttpProxy(it)
}
}
if (config.allowAllAF) {
Log.d(TAG, "allowFamily")
vpnBuilder.allowFamily(OsConstants.AF_INET)
vpnBuilder.allowFamily(OsConstants.AF_INET6)
}
Log.d(TAG, "setBlocking: ${config.blockingMode}")
vpnBuilder.setBlocking(config.blockingMode)
vpnBuilder.setUnderlyingNetworks(null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false)
}
companion object {
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
Log.d(TAG, "Extracting library: $libraryName")
val apks = hashSetOf<String>()
context.applicationInfo.run {
sourceDir?.let { apks += it }
splitSourceDirs?.let { apks += it }
}
for (abi in Build.SUPPORTED_ABIS) {
for (apk in apks) {
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
val mappedName = System.mapLibraryName(libraryName)
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
val zipEntry = zipFile.getEntry(libraryZipPath)
zipEntry?.let {
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
FileOutputStream(destination).use { outStream ->
zipFile.getInputStream(zipEntry).use { inStream ->
inStream.copyTo(outStream, 32 * 1024)
outStream.fd.sync()
}
}
}
return true
}
}
}
return false
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
fun loadSharedLibrary(context: Context, libraryName: String) {
Log.d(TAG, "Loading library: $libraryName")
try {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.d(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
if (extractLibrary(context, libraryName, tempFile)) {
System.load(tempFile.absolutePath)
return
}
} catch (e: Exception) {
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
} finally {
tempFile?.delete()
}
}
}
}
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
private fun VpnService.Builder.addRoute(addr: InetNetwork) = addRoute(addr.address, addr.mask)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun VpnService.Builder.excludeRoute(addr: InetNetwork) = excludeRoute(IpPrefix(addr.address, addr.mask))

View File

@@ -0,0 +1,113 @@
package org.amnezia.vpn.protocol
import android.net.ProxyInfo
import android.os.Build
import androidx.annotation.RequiresApi
import java.net.InetAddress
import org.amnezia.vpn.util.net.InetNetwork
open class ProtocolConfig protected constructor(
val addresses: Set<InetNetwork>,
val dnsServers: Set<InetAddress>,
val searchDomain: String?,
val routes: Set<InetNetwork>,
val excludedRoutes: Set<InetNetwork>,
val excludedApplications: Set<String>,
val httpProxy: ProxyInfo?,
val allowAllAF: Boolean,
val blockingMode: Boolean,
val mtu: Int
) {
protected constructor(builder: Builder) : this(
builder.addresses,
builder.dnsServers,
builder.searchDomain,
builder.routes,
builder.excludedRoutes,
builder.excludedApplications,
builder.httpProxy,
builder.allowAllAF,
builder.blockingMode,
builder.mtu
)
open class Builder(blockingMode: Boolean) {
internal val addresses: MutableSet<InetNetwork> = hashSetOf()
internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
internal val routes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedApplications: MutableSet<String> = hashSetOf()
internal var searchDomain: String? = null
private set
internal var httpProxy: ProxyInfo? = null
private set
internal var allowAllAF: Boolean = false
private set
internal var blockingMode: Boolean = blockingMode
private set
internal var allowSplitTunneling: Boolean = true
private set
open var mtu: Int = 0
protected set
fun addAddress(addr: InetNetwork) = apply { this.addresses += addr }
fun addAddresses(addresses: Collection<InetNetwork>) = apply { this.addresses += addresses }
fun clearAddresses() = apply { this.addresses.clear() }
fun addDnsServer(dnsServer: InetAddress) = apply { this.dnsServers += dnsServer }
fun addDnsServers(dnsServers: Collection<InetAddress>) = apply { this.dnsServers += dnsServers }
fun setSearchDomain(domain: String) = apply { this.searchDomain = domain }
fun addRoute(route: InetNetwork) = apply { this.routes += route }
fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes }
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
fun clearRoutes() = apply { this.routes.clear() }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
@RequiresApi(Build.VERSION_CODES.Q)
fun setHttpProxy(httpProxy: ProxyInfo) = apply { this.httpProxy = httpProxy }
fun setAllowAllAF(allowAllAF: Boolean) = apply { this.allowAllAF = allowAllAF }
fun setBlockingMode(blockingMode: Boolean) = apply { this.blockingMode = blockingMode }
fun disableSplitTunneling() = apply { this.allowSplitTunneling = false }
fun setMtu(mtu: Int) = apply { this.mtu = mtu }
private fun validate() {
val errorMessage = StringBuilder()
with(errorMessage) {
if (addresses.isEmpty()) appendLine("VPN interface network address not specified.")
if (routes.isEmpty()) appendLine("VPN interface route not specified.")
if (mtu == 0) appendLine("MTU not set.")
}
if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString())
}
open fun build(): ProtocolConfig = validate().run { ProtocolConfig(this@Builder) }
}
companion object {
inline fun build(blockingMode: Boolean, block: Builder.() -> Unit): ProtocolConfig =
Builder(blockingMode).apply(block).build()
}
}

View File

@@ -0,0 +1,11 @@
package org.amnezia.vpn.protocol
// keep synchronized with client/platforms/android/android_controller.h ConnectionState
enum class ProtocolState {
CONNECTED,
CONNECTING,
DISCONNECTED,
DISCONNECTING,
RECONNECTING,
UNKNOWN
}

View File

@@ -0,0 +1,50 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val RX_BYTES_KEY = "rxBytes"
private const val TX_BYTES_KEY = "txBytes"
@Suppress("DataClassPrivateConstructor")
data class Statistics private constructor(
val rxBytes: Long = 0L,
val txBytes: Long = 0L
) {
private constructor(builder: Builder) : this(builder.rxBytes, builder.txBytes)
@Suppress("SuspiciousEqualsCombination")
fun isEmpty(): Boolean = this === EMPTY_STATISTICS || this == EMPTY_STATISTICS
class Builder {
var rxBytes: Long = 0L
private set
var txBytes: Long = 0L
private set
fun setRxBytes(rxBytes: Long) = apply { this.rxBytes = rxBytes }
fun setTxBytes(txBytes: Long) = apply { this.txBytes = txBytes }
fun build(): Statistics =
if (rxBytes + txBytes == 0L) EMPTY_STATISTICS
else Statistics(this)
}
companion object {
val EMPTY_STATISTICS: Statistics = Statistics()
inline fun build(block: Builder.() -> Unit): Statistics = Builder().apply(block).build()
}
}
fun Bundle.putStatistics(statistics: Statistics) {
putLong(RX_BYTES_KEY, statistics.rxBytes)
putLong(TX_BYTES_KEY, statistics.txBytes)
}
fun Bundle.getStatistics(): Statistics =
Statistics.build {
setRxBytes(getLong(RX_BYTES_KEY))
setTxBytes(getLong(TX_BYTES_KEY))
}

View File

@@ -0,0 +1,34 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val STATE_KEY = "state"
@Suppress("DataClassPrivateConstructor")
data class Status private constructor(
val state: ProtocolState
) {
private constructor(builder: Builder) : this(builder.state)
class Builder {
lateinit var state: ProtocolState
private set
fun setState(state: ProtocolState) = apply { this.state = state }
fun build(): Status = Status(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): Status = Builder().apply(block).build()
}
}
fun Bundle.putStatus(status: Status) {
putInt(STATE_KEY, status.state.ordinal)
}
fun Bundle.getStatus(): Status =
Status.build {
setState(ProtocolState.entries[getInt(STATE_KEY)])
}

View File

@@ -0,0 +1,25 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id("property-delegate")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.qtproject.qt.android.binding"
sourceSets {
getByName("main") {
java.setSrcDirs(listOf("$qtAndroidDir/src"))
res.setSrcDirs(listOf("$qtAndroidDir/res"))
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.Dialog">
<style name="NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="Translucent" parent="NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Light"/>
</resources>
</resources>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="/" />
</paths>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="files_path" path="/"/>
</paths>

View File

@@ -1,132 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 1000,
"identityHash": "14b379f7776710b79b9d617090efe40e",
"entities": [
{
"tableName": "Profile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remotePort",
"columnName": "remotePort",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "method",
"columnName": "method",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remoteDns",
"columnName": "remoteDns",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "udpdns",
"columnName": "udpdns",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ipv6",
"columnName": "ipv6",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '14b379f7776710b79b9d617090efe40e')"
]
}
}

View File

@@ -1,46 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}

View File

@@ -5,8 +5,6 @@ pluginManagement {
google()
mavenCentral()
gradlePluginPortal()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
includeBuild("./gradle/plugins")
@@ -18,22 +16,26 @@ dependencyResolutionManagement {
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("com.android.settings") version "8.2.0"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":shadowsocks")
include(":qt")
include(":utils")
include(":protocolApi")
include(":wireguard")
include(":awg")
include(":openvpn")
include(":cloak")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties

View File

@@ -1,68 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
//apply plugin: 'com.novoda.bintray-release'
android {
buildFeatures {
aidl true
androidResources true
}
defaultConfig {
namespace "org.amnezia.vpn.shadowsocks.core"
versionCode 1
versionName "1.0.0"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
//def lifecycleVersion = '2.0.0'
def roomVersion = "2.4.3"
//def preferencexVersion = '1.0.0'
dependencies {
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.30-M1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
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.room:room-runtime:$roomVersion" // runtime
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1"
implementation "androidx.browser:browser:1.3.0-alpha01"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "com.google.android.material:material:1.2.0-alpha05"
implementation "com.google.code.gson:gson:2.8.5"
implementation "dnsjava:dnsjava:2.1.9"
implementation "com.github.kruton:jsocks:1.0.0"
implementation "com.afollestad.material-dialogs:core:2.6.0"
// api "com.takisoft.preferencex:preferencex:1.0.0"
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
api 'com.github.kruton:jsocks:1.0.0'
kapt "androidx.room:room-compiler:$roomVersion"
kapt "androidx.lifecycle:lifecycle-compiler:2.4.0"
}

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env perl
## ArchLinux install package via pacman: perl-net-cidr-lite
use strict;
use warnings;
use Net::CIDR::Lite;
my $cidr = Net::CIDR::Lite->new;
while (my $line=<>) {
$cidr->add($line);
}
foreach my $line( @{$cidr->list} ) {
print "<item>$line</item>\n";
}

View File

@@ -1,20 +0,0 @@
#!/usr/bin/python
# -*- encoding: utf8 -*-
import sys
import IPy
def main():
china_list_set = IPy.IPSet()
for line in sys.stdin:
china_list_set.add(IPy.IP(line))
# 输出结果
for ip in china_list_set:
print '<item>' + str(ip) + '</item>'
if __name__ == "__main__":
main()

View File

@@ -1,121 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pkgutil
import urlparse
import socket
import logging
from argparse import ArgumentParser
from datetime import date
__all__ = ['main']
def parse_args():
parser = ArgumentParser()
parser.add_argument('-i', '--input', dest='input', required=True,
help='path to gfwlist', metavar='GFWLIST')
parser.add_argument('-f', '--file', dest='output', required=True,
help='path to output acl', metavar='ACL')
return parser.parse_args()
def decode_gfwlist(content):
# decode base64 if have to
try:
return content.decode('base64')
except:
return content
def get_hostname(something):
try:
# quite enough for GFW
if not something.startswith('http:'):
something = 'http://' + something
r = urlparse.urlparse(something)
return r.hostname
except Exception as e:
logging.error(e)
return None
def add_domain_to_set(s, something):
hostname = get_hostname(something)
if hostname is not None:
if hostname.startswith('.'):
hostname = hostname.lstrip('.')
if hostname.endswith('/'):
hostname = hostname.rstrip('/')
if hostname:
s.add(hostname)
def parse_gfwlist(content):
gfwlist = content.splitlines(False)
domains = set()
for line in gfwlist:
if line.find('.*') >= 0:
continue
elif line.find('*') >= 0:
line = line.replace('*', '/')
if line.startswith('!'):
continue
elif line.startswith('['):
continue
elif line.startswith('@'):
# ignore white list
continue
elif line.startswith('||'):
add_domain_to_set(domains, line.lstrip('||'))
elif line.startswith('|'):
add_domain_to_set(domains, line.lstrip('|'))
elif line.startswith('.'):
add_domain_to_set(domains, line.lstrip('.'))
else:
add_domain_to_set(domains, line)
# TODO: reduce ['www.google.com', 'google.com'] to ['google.com']
return domains
def generate_acl(domains):
header ="""#
# GFW list from https://github.com/gfwlist/gfwlist/blob/master/gfwlist.txt
# updated on DATE
#
[bypass_all]
[proxy_list]
"""
header = header.replace('DATE', str(date.today()))
proxy_content = ""
ip_content = ""
for domain in sorted(domains):
try:
socket.inet_aton(domain)
ip_content += (domain + "\n")
except socket.error:
domain = domain.replace('.', '\.')
proxy_content += ('(^|\.)' + domain + '$\n')
proxy_content = header + ip_content + proxy_content
return proxy_content
def main():
args = parse_args()
with open(args.input, 'rb') as f:
content = f.read()
content = decode_gfwlist(content)
domains = parse_gfwlist(content)
acl_content = generate_acl(domains)
with open(args.output, 'wb') as f:
f.write(acl_content)
if __name__ == '__main__':
main()

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="ImpliedQuantity" severity="warning" />
<issue id="ExtraTranslation" severity="warning" />
<issue id="MissingDefaultResource" severity="warning" />
<issue id="MissingTranslation" severity="informational" />
</lint>

View File

@@ -1,131 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingLeanbackLauncher">
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
tools:targetApi="n">
<meta-data
android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg" />
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ShadowsocksVpnService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:label="@string/app_name"-->
<!-- android:permission="android.permission.BIND_VPN_SERVICE"-->
<!-- android:process=":BG"-->
<!-- tools:targetApi="n">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.net.VpnService" />-->
<!-- </intent-filter>-->
<!-- </service>-->
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.TransproxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ProxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<!-- <activity-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.VpnRequestActivity"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:launchMode="singleTask"-->
<!-- android:taskAffinity=""-->
<!-- android:theme="@style/Theme.AppCompat.Translucent" />-->
<receiver
android:name="org.amnezia.vpn.shadowsocks.core.BootReceiver"
android:directBootAware="true"
android:enabled="false"
android:process=":QtOnlyProcess"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/AndroidManifest.xml -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="androidx.work.impl.WorkManagerInitializer"
tools:node="remove" />
<service
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
</application>
</manifest>

View File

@@ -1,13 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback;
interface IShadowsocksService {
int getState();
String getProfileName();
void registerCallback(in IShadowsocksServiceCallback cb);
void startListeningForBandwidth(in IShadowsocksServiceCallback cb, long timeout);
oneway void stopListeningForBandwidth(in IShadowsocksServiceCallback cb);
oneway void unregisterCallback(in IShadowsocksServiceCallback cb);
}

View File

@@ -1,18 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats;
//"oneway" unexpected. xinlake
interface IShadowsocksServiceCallback {
oneway void stateChanged(int state, String profileName, String msg);
oneway void trafficUpdated(long profileId, in TrafficStats stats);
// Traffic data has persisted to database, listener should refetch their data from database
oneway void trafficPersisted(long profileId);
}
//oneway interface IShadowsocksServiceCallback {
// void stateChanged(int state, String profileName, String msg);
// void trafficUpdated(long profileId, in TrafficStats stats);
// // Traffic data has persisted to database, listener should refetch their data from database
// void trafficPersisted(long profileId);
//}

View File

@@ -1,3 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
parcelable TrafficStats;

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
[proxy_all]
[bypass_list]
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More