mirror of
https://github.com/TrustTunnel/TrustTunnelClient.git
synced 2026-05-22 19:41:36 +00:00
Pull request 574: TRUST-228 lint part 1
Squashed commit of the following:
commit 3e269033f16e4672860c0fe24cbe8df4b09065f4
Author: Sergey Fionov <sfionov@adguard.com>
Date: Wed Dec 24 20:09:14 2025 +0300
Applied suggestion
commit 14468d33f3ae87accce3a2f2a5c31744a13ac70c
Merge: e359993e 305f5b9a
Author: Andrey Meshkov <am@adguard.com>
Date: Wed Dec 24 20:06:20 2025 +0300
Merge with master
commit e359993e58219942ed5515e54177a8c0250d5efe
Author: Andrey Meshkov <am@adguard.com>
Date: Wed Dec 24 19:59:48 2025 +0300
Fixed python issue
commit 652c8481c7707cf8f8ef7743009fafd7597b280c
Author: Andrey Meshkov <am@adguard.com>
Date: Wed Dec 24 19:47:57 2025 +0300
clang-format
commit d43bf98b664a1136eb28c93304c0fe6b6bf1f247
Author: Andrey Meshkov <am@adguard.com>
Date: Wed Dec 24 19:47:47 2025 +0300
Added lint targets
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"no-inline-html": {
|
||||
"allowed_elements": [
|
||||
"a"
|
||||
"a", "p", "picture", "source", "img"
|
||||
]
|
||||
},
|
||||
"no-trailing-spaces": {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
third-party
|
||||
fastlane
|
||||
env
|
||||
+50
-50
@@ -2,159 +2,159 @@
|
||||
|
||||
## 0.99.63
|
||||
|
||||
* [Features] Add option to allow inbound connections to the specified UDP/TCP ports when
|
||||
- [Features] Add option to allow inbound connections to the specified UDP/TCP ports when
|
||||
`ag::VpnWinTunnelSettings::block_untunneled` is enabled.
|
||||
* See `ag::VpnWinTunnelSettings::block_untunneled_exclude_ports`.
|
||||
- See `ag::VpnWinTunnelSettings::block_untunneled_exclude_ports`.
|
||||
|
||||
## 0.95.31
|
||||
|
||||
* [Feature] IPv6 support must now be explicitly specified on each `VpnEndpoint`. Previously, the library assumed
|
||||
- [Feature] IPv6 support must now be explicitly specified on each `VpnEndpoint`. Previously, the library assumed
|
||||
that all endpoints in a location have IPv6 support if any of the endpoints in a location had an IPv6 address.
|
||||
* See `VpnEndpoint::has_ipv6`.
|
||||
- See `VpnEndpoint::has_ipv6`.
|
||||
|
||||
## 0.94.7
|
||||
|
||||
* [Feature] Add an option to use a post-quantum group for key exchange in TLS handshakes.
|
||||
* See `vpn_post_quantum_group_set_enabled`.
|
||||
- [Feature] Add an option to use a post-quantum group for key exchange in TLS handshakes.
|
||||
- See `vpn_post_quantum_group_set_enabled`.
|
||||
|
||||
## 0.93.28
|
||||
|
||||
* Handler profiling is now disabled by default.
|
||||
- Handler profiling is now disabled by default.
|
||||
|
||||
## 0.93.18
|
||||
|
||||
* [Feature] Add an option to profile VPN handler execution: if enabled, a warning will be written to the log
|
||||
- [Feature] Add an option to profile VPN handler execution: if enabled, a warning will be written to the log
|
||||
whenever a handler call is taking too long. Profiling is enabled by default. Applications might want to disable
|
||||
it when running in production.
|
||||
* See `ag::vpn_handler_profiling_set_enabled`.
|
||||
- See `ag::vpn_handler_profiling_set_enabled`.
|
||||
|
||||
## 0.92.182
|
||||
|
||||
* [Feature] The library now notify an application with information about connection. This event contain info
|
||||
- [Feature] The library now notify an application with information about connection. This event contain info
|
||||
about source ip, destination (ip, domain or both), transport protocol and action (bypass/tunnel).
|
||||
For this purpose, new event `VPN_EVENT_CONNECTION_INFO` was introduced in `VpnEvent`.
|
||||
|
||||
## 0.92.115
|
||||
|
||||
* Added a new `VpnConnectAction`: `VPN_CA_REJECT`.
|
||||
- Added a new `VpnConnectAction`: `VPN_CA_REJECT`.
|
||||
|
||||
## 0.92.107
|
||||
|
||||
* Added `VpnConnectedInfo::relay_address`.
|
||||
- Added `VpnConnectedInfo::relay_address`.
|
||||
|
||||
## 0.92.100
|
||||
|
||||
* Added `VpnEndpoint::remote_id`. See the field's doc for details.
|
||||
- Added `VpnEndpoint::remote_id`. See the field's doc for details.
|
||||
|
||||
## 0.92.94
|
||||
|
||||
* Changes in pinging behaviour.
|
||||
* See the updated doc comments for the fields of `LocationsPingerInfo` and `PingInfo`, `locations_pinger_start`, `ping_start` for details.
|
||||
* The pinging timeout is now per connection attempt, NOT for the whole pinging procedure. Applications may need to adjust.
|
||||
- Changes in pinging behaviour.
|
||||
- See the updated doc comments for the fields of `LocationsPingerInfo` and `PingInfo`, `locations_pinger_start`, `ping_start` for details.
|
||||
- The pinging timeout is now per connection attempt, NOT for the whole pinging procedure. Applications may need to adjust.
|
||||
|
||||
## 0.92.90
|
||||
|
||||
* Changes in pinging and locations API.
|
||||
* Removed `VpnUpstreamConfig::relay_addresses` and `LocationsPingerInfo::relay_address`.
|
||||
* Added `VpnLocation::relay_addresses`.
|
||||
* When a `VpnLocation` is used as part of a `VpnUpstreamConfig`, its relay addresses shall be used
|
||||
- Changes in pinging and locations API.
|
||||
- Removed `VpnUpstreamConfig::relay_addresses` and `LocationsPingerInfo::relay_address`.
|
||||
- Added `VpnLocation::relay_addresses`.
|
||||
- When a `VpnLocation` is used as part of a `VpnUpstreamConfig`, its relay addresses shall be used
|
||||
exactly in the same manner as `VpnUpstreamConfig::relay_addresses` were used before.
|
||||
* The documentation for `ag::locations_pinger_start` has been updated to include a note about how
|
||||
- The documentation for `ag::locations_pinger_start` has been updated to include a note about how
|
||||
the location's relay addresses are used by the pinger.
|
||||
|
||||
## 0.92.88
|
||||
|
||||
* [Feature] Support connecting to endpoints through a set of SNI proxies.
|
||||
* `VpnUpstreamConfig::relay_addresses` can now be specified when connecting to a location.
|
||||
- [Feature] Support connecting to endpoints through a set of SNI proxies.
|
||||
- `VpnUpstreamConfig::relay_addresses` can now be specified when connecting to a location.
|
||||
The client shall try using one of the relay addresses to connect to an endpoint if it's unavailable
|
||||
on its normal address. The client shall automatically disqualify relay addresses that don't work.
|
||||
* `LocationsPingerInfo::relay_address` can now be specified when pinging a location. The pinger shall try
|
||||
- `LocationsPingerInfo::relay_address` can now be specified when pinging a location. The pinger shall try
|
||||
to use it if an endpoint is unavailable on its normal address. `PingResult::through_relay` will be
|
||||
non-zero if the relay is used. The application should try pinging with a different relay address if
|
||||
there are pinging errors through the relay.
|
||||
* `LocationsPinger` will now send ClientHello/QUIC Initial to "ping" the endpoints.
|
||||
* `LocationsPinger::anti_dpi` can now be specified to enable or disable anti-DPI measures during pinging.
|
||||
- `LocationsPinger` will now send ClientHello/QUIC Initial to "ping" the endpoints.
|
||||
- `LocationsPinger::anti_dpi` can now be specified to enable or disable anti-DPI measures during pinging.
|
||||
|
||||
## 0.92.74
|
||||
|
||||
* [Feature] The library now notifies an application about the amount of traffic passed through
|
||||
- [Feature] The library now notifies an application about the amount of traffic passed through
|
||||
connections that have been routed through an endpoint.
|
||||
For this purpose, two events were introduced in `VpnEvent`:
|
||||
* `VPN_EVENT_TUNNEL_CONNECTION_STATS` - raised only for connections that have been routed through
|
||||
- `VPN_EVENT_TUNNEL_CONNECTION_STATS` - raised only for connections that have been routed through
|
||||
an endpoint,
|
||||
* `VPN_EVENT_TUNNEL_CONNECTION_CLOSED` - raised for any user connection.
|
||||
- `VPN_EVENT_TUNNEL_CONNECTION_CLOSED` - raised for any user connection.
|
||||
|
||||
## 0.92.46
|
||||
|
||||
* [Feature] The library now accepts CIDR range in exclusion list.
|
||||
- [Feature] The library now accepts CIDR range in exclusion list.
|
||||
|
||||
## 0.92.28
|
||||
|
||||
* Removed `vpn_network_manager_update_tun_interface_dns()` as redundant.
|
||||
- Removed `vpn_network_manager_update_tun_interface_dns()` as redundant.
|
||||
|
||||
## 0.92.23
|
||||
|
||||
* The library now accepts a list of DNS upstreams instead of a single one.
|
||||
* `dns_upsteam` field of `VpnListenerConfig` renamed to `dns_upsteams`.
|
||||
* `dns_upsteam` field removed from `VpnDnsUpstreamUnavailableEvent`.
|
||||
- The library now accepts a list of DNS upstreams instead of a single one.
|
||||
- `dns_upsteam` field of `VpnListenerConfig` renamed to `dns_upsteams`.
|
||||
- `dns_upsteam` field removed from `VpnDnsUpstreamUnavailableEvent`.
|
||||
|
||||
## 0.92.11
|
||||
|
||||
* [Feature] `h3://` scheme is now allowed for DNS upstream.
|
||||
- [Feature] `h3://` scheme is now allowed for DNS upstream.
|
||||
|
||||
## 0.91.88
|
||||
|
||||
* DNS queries are now routed according to VPN settings. I.e., queries with domains
|
||||
- DNS queries are now routed according to VPN settings. I.e., queries with domains
|
||||
matching exclusions are routed directly to the target resolver in the general mode,
|
||||
but queries not matching exclusions are routed through the endpoint. To do it correctly
|
||||
the library needs to know the system (see `dns_manager_set_tunnel_interface_servers()`)
|
||||
and TUN interface DNS servers (see `vpn_network_manager_update_tun_interface_dns()`).
|
||||
* The library now has one centralized point for setting the outbound network
|
||||
- The library now has one centralized point for setting the outbound network
|
||||
interface for I/O operations - `vpn_network_manager_set_outbound_interface()`.
|
||||
* [Windows] Use the method above instead of the removed `vpn_win_set_bound_if()`.
|
||||
- [Windows] Use the method above instead of the removed `vpn_win_set_bound_if()`.
|
||||
|
||||
## 0.91.82
|
||||
|
||||
* [Fix] Introduced an error code indicating that no connection attempts left
|
||||
- [Fix] Introduced an error code indicating that no connection attempts left
|
||||
after initial connect() call `VPN_EC_INITIAL_CONNECT_FAILED`.
|
||||
|
||||
## 0.91.45
|
||||
|
||||
* [Changed] `Location unavailable` semantics:
|
||||
* It is now considered as a fatal error, i.e. the client goes in the disconnected state.
|
||||
- [Changed] `Location unavailable` semantics:
|
||||
- It is now considered as a fatal error, i.e. the client goes in the disconnected state.
|
||||
It is up to application to refresh a location data and restart the client.
|
||||
* It is now raised only after the client receives the abandon command.
|
||||
- It is now raised only after the client receives the abandon command.
|
||||
It is up to application to detect infinite recovery loop in case there are some
|
||||
connectivity issues.
|
||||
* [Changed] [Windows] `vpn_abandon_endpoint()` now takes an endpoint as a parameter
|
||||
- [Changed] [Windows] `vpn_abandon_endpoint()` now takes an endpoint as a parameter
|
||||
|
||||
## 0.91.20
|
||||
|
||||
* [Changed] [Windows] Calling `vpn_win_set_bound_if()` now turns off the socket protection instead of
|
||||
- [Changed] [Windows] Calling `vpn_win_set_bound_if()` now turns off the socket protection instead of
|
||||
detecting an active network interface by itself. It's up to the application to call the new method
|
||||
`vpn_win_detect_active_if()` and pass its result to `vpn_win_set_bound_if()` to activate the socket
|
||||
protection.
|
||||
|
||||
## 0.91.10
|
||||
|
||||
* [Feature] Added Wintun support. Dll downloaded from www.wintun.net is needed for standalone_client to run under Windows.
|
||||
- [Feature] Added Wintun support. Dll downloaded from www.wintun.net is needed for standalone_client to run under Windows.
|
||||
|
||||
## 0.90.15
|
||||
|
||||
* [Feature] Route QUIC connections according to the VPN mode instead of always dropping or redirecting them
|
||||
- [Feature] Route QUIC connections according to the VPN mode instead of always dropping or redirecting them
|
||||
|
||||
## 0.90.13
|
||||
|
||||
* [Fix] Fix leaks and memory bugs in `tls_serialize_cert_chain`, `tls_free_serialized_chain`. Add tests.
|
||||
- [Fix] Fix leaks and memory bugs in `tls_serialize_cert_chain`, `tls_free_serialized_chain`. Add tests.
|
||||
|
||||
## 0.90.12
|
||||
|
||||
* [Fix] Change signature of some exported functions to be more consistent with the rest and simpler for C# bindings.
|
||||
- [Fix] Change signature of some exported functions to be more consistent with the rest and simpler for C# bindings.
|
||||
|
||||
## 0.90.6
|
||||
|
||||
* [Fix] Fix version increment script.
|
||||
- [Fix] Fix version increment script.
|
||||
|
||||
## 0.90.4
|
||||
|
||||
* [Feature] VpnLibs is now open-source.
|
||||
- [Feature] VpnLibs is now open-source.
|
||||
|
||||
@@ -14,15 +14,24 @@ ifeq ($(origin MSVC_YEAR), undefined)
|
||||
endif
|
||||
BUILD_DIR = build
|
||||
EXPORT_DIR ?= bin
|
||||
SETUP_WIZARD_DIR = trusttunnel/setup_wizard
|
||||
|
||||
.PHONY: init
|
||||
## Initialize the development environment (git hooks, etc.)
|
||||
init:
|
||||
git config core.hooksPath ./scripts/hooks
|
||||
|
||||
.PHONY: bootstrap_deps
|
||||
## Export all the required conan packages to the local cache
|
||||
bootstrap_deps:
|
||||
python3 -m venv env && \
|
||||
. env/bin/activate && \
|
||||
pip install -r requirements.txt && \
|
||||
./scripts/bootstrap_conan_deps.py
|
||||
|
||||
.PHONY: build_libs
|
||||
## Build the libraries
|
||||
build_libs: bootstrap_deps
|
||||
.PHONY: setup_cmake
|
||||
## Setup CMake
|
||||
setup_cmake: bootstrap_deps
|
||||
ifeq ($(OS), Windows_NT)
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ^
|
||||
-DCMAKE_C_FLAGS_DEBUG=/MT ^
|
||||
@@ -32,12 +41,16 @@ ifeq ($(OS), Windows_NT)
|
||||
else
|
||||
mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && \
|
||||
cmake -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \
|
||||
-DCMAKE_C_COMPILER="clang" \
|
||||
-DCMAKE_CXX_COMPILER="clang++" \
|
||||
-DCMAKE_CXX_FLAGS="-stdlib=libc++" \
|
||||
-GNinja \
|
||||
..
|
||||
-DCMAKE_C_COMPILER="clang" \
|
||||
-DCMAKE_CXX_COMPILER="clang++" \
|
||||
-DCMAKE_CXX_FLAGS="-stdlib=libc++" \
|
||||
-GNinja \
|
||||
..
|
||||
endif
|
||||
|
||||
.PHONY: build_libs
|
||||
## Build the libraries
|
||||
build_libs: setup_cmake
|
||||
cmake --build $(BUILD_DIR) --target vpnlibs_core
|
||||
|
||||
.PHONY: build_trusttunnel_client
|
||||
@@ -62,15 +75,78 @@ build_and_export_bin: build_trusttunnel_client build_wizard
|
||||
$(EXPORT_DIR)
|
||||
@echo "Binaries are stored in $(EXPORT_DIR)"
|
||||
|
||||
.PHONY: lint-md
|
||||
## Lint markdown files.
|
||||
## `markdownlint-cli` should be installed:
|
||||
## macOS: `brew install markdownlint-cli`
|
||||
## Linux: `npm install -g markdownlint-cli`
|
||||
lint-md:
|
||||
markdownlint README.md trusttunnel/README.md
|
||||
|
||||
.PHONY: clean
|
||||
## Clean the project
|
||||
clean:
|
||||
cmake --build $(BUILD_DIR) --target clean
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-md lint-rust lint-cpp
|
||||
|
||||
## Lint c++ files.
|
||||
## TODO: enable clang-tidy
|
||||
.PHONY: lint-cpp
|
||||
lint-cpp: clang-format
|
||||
|
||||
## Check c++ code formatting with clang-format.
|
||||
.PHONY: clang-format
|
||||
clang-format:
|
||||
find . \
|
||||
-path './third-party' -prune -o \
|
||||
-name '*.c' -o -name '*.cpp' -o -name '*.h' \
|
||||
-print | xargs clang-format -n -Werror
|
||||
|
||||
## Check c++ code formatting with clang-tidy.
|
||||
.PHONY: clang-tidy
|
||||
clang-tidy:
|
||||
cmake -S . -B $(BUILD_DIR) -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
run-clang-tidy -p $(BUILD_DIR) '^(?!.*(/third-party/)).*\.cpp$$'
|
||||
|
||||
## Lint markdown files.
|
||||
## `markdownlint-cli` should be installed:
|
||||
## macOS: `brew install markdownlint-cli`
|
||||
## Linux: `npm install -g markdownlint-cli`
|
||||
.PHONY: lint-md
|
||||
lint-md:
|
||||
markdownlint .
|
||||
|
||||
## Check Rust code formatting with rustfmt.
|
||||
## `rustfmt` should be installed:
|
||||
## rustup component add rustfmt
|
||||
.PHONY: lint-rust
|
||||
lint-rust:
|
||||
cargo fmt --all --manifest-path $(SETUP_WIZARD_DIR)/Cargo.toml -- --check
|
||||
|
||||
## Fix linter issues that are auto-fixable.
|
||||
.PHONY: lint-fix
|
||||
lint-fix: lint-fix-rust lint-fix-md lint-fix-cpp
|
||||
|
||||
## Auto-fix c++ formatting with clang-format.
|
||||
.PHONY: lint-fix-cpp
|
||||
lint-fix-cpp:
|
||||
find . \
|
||||
-path './third-party' -prune -o \
|
||||
-name '*.c' -o -name '*.cpp' -o -name '*.h' \
|
||||
-print | xargs clang-format -i
|
||||
|
||||
## Auto-fix Rust code formatting issues with rustfmt.
|
||||
.PHONY: lint-fix-rust
|
||||
lint-fix-rust:
|
||||
cargo fmt --all --manifest-path $(SETUP_WIZARD_DIR)/Cargo.toml
|
||||
|
||||
## Auto-fix markdown files.
|
||||
.PHONY: lint-fix-md
|
||||
lint-fix-md:
|
||||
markdownlint --fix .
|
||||
|
||||
.PHONY: test
|
||||
test: test-cpp test-rust
|
||||
|
||||
.PHONY: test-cpp
|
||||
test-cpp: build_libs
|
||||
cmake --build $(BUILD_DIR) --target tests
|
||||
ctest --test-dir $(BUILD_DIR)
|
||||
|
||||
.PHONY: test-rust
|
||||
test-rust:
|
||||
cargo test --workspace --manifest-path $(SETUP_WIZARD_DIR)/Cargo.toml
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.adguardcdn.com/website/github.com/TrustTunnel/logo_dark.svg" width="300px" alt="TrustTunnel" />
|
||||
@@ -5,8 +6,6 @@
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
# <p align="center">TrustTunnel Client</p>
|
||||
|
||||
<p align="center">Free, fast, open-source and secure client for the TrustTunnel VPN</p>
|
||||
|
||||
<p align="center"><a href="https://github.com/TrustTunnel/TrustTunnel">Endpoint</a>
|
||||
@@ -85,6 +84,14 @@ Once you have obtained the exported endpoint configuration for the client, refer
|
||||
- Windows (Chocolatey): `choco install cmake`
|
||||
- LLVM 17 or higher
|
||||
- macOS: `brew install llvm`
|
||||
- Add `clang-format` and `clang-tidy` to PATH:
|
||||
|
||||
```shell
|
||||
ln -s /opt/homebrew/opt/llvm/bin/clang-format /opt/homebrew/bin/clang-format
|
||||
ln -s /opt/homebrew/opt/llvm/bin/clang-tidy /opt/homebrew/bin/clang-tidy
|
||||
ln -s /opt/homebrew/opt/llvm/bin/run-clang-tidy /opt/homebrew/bin/run-clang-tidy
|
||||
```
|
||||
|
||||
- Linux (Debian/Ubuntu): `apt install llvm clang libc++-dev`
|
||||
- Windows (Chocolatey): `choco install llvm`
|
||||
- Conan 2.0.5 or higher
|
||||
@@ -113,21 +120,9 @@ Once you have obtained the exported endpoint configuration for the client, refer
|
||||
|
||||
### Building
|
||||
|
||||
#### For python externally managed environments
|
||||
#### Prepare developer environment
|
||||
|
||||
You might get an error where Python will report some missing modules such as
|
||||
|
||||
```shell
|
||||
ModuleNotFoundError: No module named 'yaml'
|
||||
```
|
||||
|
||||
Please use following commands:
|
||||
|
||||
```shell
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
pip3 install -r ./scripts/requirements.txt
|
||||
```
|
||||
Run `make init` to prepare the developer environment and set up git hooks.
|
||||
|
||||
#### Using Makefile
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/qos.h>
|
||||
#include <TargetConditionals.h>
|
||||
#include <sys/qos.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
@@ -29,7 +29,7 @@ struct VpnError {
|
||||
typedef enum {
|
||||
VPN_UP_HTTP2, // HTTP/2 only.
|
||||
VPN_UP_HTTP3, // HTTP/3, with mandatory fallback to HTTP/2.
|
||||
VPN_UP_AUTO, // VpnEndpoint: Try HTTP/2 and HTTP/3 simultaneously, use the first successful connection.
|
||||
VPN_UP_AUTO, // VpnEndpoint: Try HTTP/2 and HTTP/3 simultaneously, use the first successful connection.
|
||||
// VpnUpstreamConfig: Use endpoints' preferred protocol.
|
||||
} VpnUpstreamProtocol;
|
||||
|
||||
@@ -59,8 +59,7 @@ struct TcpFlowCtrlInfo {
|
||||
|
||||
// May be owning or non-owning depending on context
|
||||
typedef AG_ARRAY_OF(const char) VpnStr;
|
||||
#define VPNSTR_INIT(c_string) \
|
||||
{ c_string, (c_string) ? uint32_t(strlen(c_string)) : 0 }
|
||||
#define VPNSTR_INIT(c_string) {c_string, (c_string) ? uint32_t(strlen(c_string)) : 0}
|
||||
|
||||
// QUIC defaults
|
||||
static constexpr size_t QUIC_LOCAL_CONN_ID_LEN = 16;
|
||||
@@ -135,8 +134,8 @@ class VpnPacketsHolder {
|
||||
public:
|
||||
VpnPacketsHolder() = default;
|
||||
explicit VpnPacketsHolder(VpnPackets packets)
|
||||
: m_packets(packets.data, packets.data + packets.size)
|
||||
{}
|
||||
: m_packets(packets.data, packets.data + packets.size) {
|
||||
}
|
||||
~VpnPacketsHolder() {
|
||||
for (auto p : m_packets) {
|
||||
if (p.destructor) {
|
||||
@@ -164,6 +163,7 @@ public:
|
||||
std::swap(m_packets, other.m_packets);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<VpnPacket> m_packets;
|
||||
};
|
||||
@@ -172,7 +172,7 @@ private:
|
||||
* Convert milliseconds to timeval structure
|
||||
*/
|
||||
static inline struct timeval ms_to_timeval(uint32_t ms) {
|
||||
struct timeval tv {}; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
struct timeval tv{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_usec = (ms % 1000) * 1000;
|
||||
return tv;
|
||||
@@ -304,7 +304,7 @@ uint32_t ntoh_24(uint32_t x);
|
||||
/**
|
||||
* Just like `std::remove_if()`, but swaps elements to the tail instead of moving them
|
||||
*/
|
||||
template<typename Iterator, typename Predicate>
|
||||
template <typename Iterator, typename Predicate>
|
||||
Iterator swap_remove_if(Iterator begin, Iterator end, Predicate p) {
|
||||
begin = std::find_if(begin, end, p);
|
||||
if (begin != end) {
|
||||
|
||||
+5
-5
@@ -8,11 +8,11 @@ import re
|
||||
|
||||
class VpnLibsConan(ConanFile):
|
||||
name = "vpn-libs"
|
||||
license = "GPL-3.0-or-later"
|
||||
author = "AdguardTeam"
|
||||
url = "https://github.com/AdguardTeam/VpnLibs"
|
||||
vcs_url = "https://github.com/AdguardTeam/VpnLibs.git"
|
||||
description = "A VPN client library that provides client network traffic tunnelling to an AdGuard VPN server"
|
||||
license = "Apache-2.0"
|
||||
author = "TrustTunnel"
|
||||
url = "https://github.com/TrustTunnel/TrustTunnelClient"
|
||||
vcs_url = "https://github.com/TrustTunnel/TrustTunnelClient.git"
|
||||
description = "TrustTunnel client implementation"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
options = {
|
||||
"with_ghc": [True, False],
|
||||
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
std::string text;
|
||||
MatchFlagsSet flags;
|
||||
};
|
||||
struct DomainEntryMalformed{};
|
||||
struct DomainEntryMalformed {};
|
||||
using ParseResult = std::variant<SocketAddress, CidrRange, DomainEntryInfo, DomainEntryMalformed>;
|
||||
|
||||
VpnMode m_mode = VPN_MODE_GENERAL;
|
||||
|
||||
@@ -23,7 +23,7 @@ enum ServerEvent {
|
||||
*/
|
||||
SERVER_EVENT_READ, /**< Called when some data needs to be sent via connection (raised with `ServerReadEvent`) */
|
||||
SERVER_EVENT_DATA_SENT, /**< Called when some data was sent to client (raised with `ServerDataSentEvent`) */
|
||||
SERVER_EVENT_HEALTH_CHECK_ERROR, /**< Called when a health check result has failed (raised with `VpnError`) */
|
||||
SERVER_EVENT_HEALTH_CHECK_ERROR, /**< Called when a health check result has failed (raised with `VpnError`) */
|
||||
SERVER_EVENT_GET_AVAILABLE_TO_SEND, /**< Called when the upstream wants to know available size for sending (raised
|
||||
with `ServerAvailableToSendEvent`) */
|
||||
SERVER_EVENT_ERROR, /**< Called when some error happened on server side (raised with `ServerError`) */
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/net_utils.h"
|
||||
#include "common/utils.h"
|
||||
#include "common/socket_address.h"
|
||||
#include "common/utils.h"
|
||||
#include "net/tcp_socket.h"
|
||||
#include "net/udp_socket.h"
|
||||
#include "vpn/platform.h"
|
||||
@@ -105,8 +105,7 @@ inline bool operator==(const TunnelAddressPair &lh, const TunnelAddressPair &rh)
|
||||
if (lh.dst.index() != rh.dst.index()) {
|
||||
return false;
|
||||
}
|
||||
if (const SocketAddress *ld = std::get_if<SocketAddress>(&lh.dst),
|
||||
*rd = std::get_if<SocketAddress>(&rh.dst);
|
||||
if (const SocketAddress *ld = std::get_if<SocketAddress>(&lh.dst), *rd = std::get_if<SocketAddress>(&rh.dst);
|
||||
ld && rd) {
|
||||
return *ld == *rd;
|
||||
}
|
||||
@@ -228,16 +227,14 @@ struct hash<ag::TunnelAddress> {
|
||||
template <>
|
||||
struct hash<ag::TunnelAddressPair> {
|
||||
size_t operator()(const ag::TunnelAddressPair &addr) const {
|
||||
return size_t(
|
||||
ag::hash_pair_combine(ag::socket_address_hash(addr.src), hash<ag::TunnelAddress>{}(addr.dst)));
|
||||
return size_t(ag::hash_pair_combine(ag::socket_address_hash(addr.src), hash<ag::TunnelAddress>{}(addr.dst)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<ag::SockAddrTag> {
|
||||
size_t operator()(const ag::SockAddrTag &k) const {
|
||||
return size_t(
|
||||
ag::hash_pair_combine(ag::socket_address_hash(k.addr), std::hash<std::string>()(k.appname)));
|
||||
return size_t(ag::hash_pair_combine(ag::socket_address_hash(k.addr), std::hash<std::string>()(k.appname)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "common/socket_address.h"
|
||||
#include "net/locations_pinger.h"
|
||||
#include "net/network_manager.h"
|
||||
#include "net/utils.h"
|
||||
#include "net/tcp_socket.h"
|
||||
#include "net/quic_connector.h"
|
||||
#include "net/tcp_socket.h"
|
||||
#include "net/utils.h"
|
||||
#include "vpn/event_loop.h"
|
||||
#include "vpn/fsm.h"
|
||||
#include "vpn/internal/client_listener.h"
|
||||
|
||||
+27
-26
@@ -48,18 +48,18 @@ typedef enum {
|
||||
VPN_EC_NOERROR, // Depending on context may mean successful operation status (if returned from `vpn_connect`)
|
||||
// or an endpoint session is closed without any error (if raised with `VPN_EVENT_STATE_CHANGED`)
|
||||
VPN_EC_ERROR, // General code for the errors not described below
|
||||
VPN_EC_INVALID_SETTINGS, // Settings passed for an operation are invalid
|
||||
VPN_EC_ADDR_IN_USE, // Operation failed because the specified address was in use
|
||||
VPN_EC_INVALID_STATE, // VPN client instance is in invalid state for the requested operation
|
||||
VPN_EC_INVALID_SETTINGS, // Settings passed for an operation are invalid
|
||||
VPN_EC_ADDR_IN_USE, // Operation failed because the specified address was in use
|
||||
VPN_EC_INVALID_STATE, // VPN client instance is in invalid state for the requested operation
|
||||
// Recoverable runtime errors. Client may update data from backend and reconnect.
|
||||
VPN_EC_AUTH_REQUIRED, // Authorization error (in case user credentials are invalid or expired)
|
||||
VPN_EC_LOCATION_UNAVAILABLE, // None of the endpoints in a location are available
|
||||
VPN_EC_AUTH_REQUIRED, // Authorization error (in case user credentials are invalid or expired)
|
||||
VPN_EC_LOCATION_UNAVAILABLE, // None of the endpoints in a location are available
|
||||
// Unrecoverable runtime errors. Client should NOT reconnect automatically.
|
||||
VPN_EC_EVENT_LOOP_FAILURE, // Failed to start the IO event loop, or it unexpectedly terminated
|
||||
VPN_EC_INITIAL_CONNECT_FAILED, // No connection attempts left after initial connect() call
|
||||
VPN_EC_FATAL_CONNECTIVITY_ERROR, // Fired after ConnectivityError callback with fatal error code (e.g. too many devices)
|
||||
// In this case client should not reconnect automatically, and instead show user an error
|
||||
// And properly tear down both tunnel and VPN client.
|
||||
VPN_EC_EVENT_LOOP_FAILURE, // Failed to start the IO event loop, or it unexpectedly terminated
|
||||
VPN_EC_INITIAL_CONNECT_FAILED, // No connection attempts left after initial connect() call
|
||||
VPN_EC_FATAL_CONNECTIVITY_ERROR, // Fired after ConnectivityError callback with fatal error code (e.g. too many
|
||||
// devices) In this case client should not reconnect automatically, and instead
|
||||
// show user an error And properly tear down both tunnel and VPN client.
|
||||
} VpnErrorCode;
|
||||
|
||||
typedef struct Vpn Vpn;
|
||||
@@ -78,7 +78,8 @@ typedef struct {
|
||||
*/
|
||||
evutil_socket_t fd;
|
||||
/**
|
||||
* For non-fd mode, if specified, then `VPN_EVENT_CLIENT_OUTPUT is not needed - it will be automatically passed to this tunnel.
|
||||
* For non-fd mode, if specified, then `VPN_EVENT_CLIENT_OUTPUT is not needed - it will be automatically passed to
|
||||
* this tunnel.
|
||||
*/
|
||||
VpnOsTunnel *tunnel;
|
||||
/** Maximum transfer unit for TCP protocol (if 0, `DEFAULT_MTU_SIZE` will be used) */
|
||||
@@ -262,8 +263,8 @@ typedef enum {
|
||||
} VpnEvent;
|
||||
|
||||
typedef struct {
|
||||
X509 *cert; // Certificate to verify
|
||||
STACK_OF(X509) *chain; // Untrusted chain
|
||||
X509 *cert; // Certificate to verify
|
||||
STACK_OF(X509) * chain; // Untrusted chain
|
||||
/**
|
||||
* SET BY HANDLER: Outcome of the operation (0 if successful, `VPN_SKIP_VERIFICATION_FLAG` to indicate that
|
||||
* hostname verification should be skipped)
|
||||
@@ -315,10 +316,10 @@ typedef struct {
|
||||
} VpnWaitingRecoveryInfo;
|
||||
|
||||
typedef struct {
|
||||
const VpnEndpoint *endpoint; // the endpoint to which the library is connected
|
||||
const VpnRelay *relay; // non-null if connected through a relay
|
||||
VpnUpstreamProtocol protocol; // the protocol used for this connection
|
||||
const char *kex_group; // name of group function used for key exchange
|
||||
const VpnEndpoint *endpoint; // the endpoint to which the library is connected
|
||||
const VpnRelay *relay; // non-null if connected through a relay
|
||||
VpnUpstreamProtocol protocol; // the protocol used for this connection
|
||||
const char *kex_group; // name of group function used for key exchange
|
||||
} VpnConnectedInfo;
|
||||
|
||||
typedef struct {
|
||||
@@ -385,20 +386,20 @@ typedef struct {
|
||||
} VpnTunnelConnectionClosedEvent;
|
||||
|
||||
typedef enum {
|
||||
VPN_FCA_BYPASS, // Route the connection directly to the destination host
|
||||
VPN_FCA_TUNNEL, // Route the connection through the VPN endpoint
|
||||
VPN_FCA_REJECT, // Reject the connection
|
||||
VPN_FCA_BYPASS, // Route the connection directly to the destination host
|
||||
VPN_FCA_TUNNEL, // Route the connection through the VPN endpoint
|
||||
VPN_FCA_REJECT, // Reject the connection
|
||||
} VpnFinalConnectionAction;
|
||||
|
||||
/**
|
||||
* VPN client connection information
|
||||
*/
|
||||
typedef struct {
|
||||
const SocketAddressStorage *src; // source address of connection
|
||||
const SocketAddressStorage *dst; // destination address of connection
|
||||
const char *domain; // destination domain
|
||||
int proto; // connection protocol
|
||||
VpnFinalConnectionAction action; // final action
|
||||
const SocketAddressStorage *src; // source address of connection
|
||||
const SocketAddressStorage *dst; // destination address of connection
|
||||
const char *domain; // destination domain
|
||||
int proto; // connection protocol
|
||||
VpnFinalConnectionAction action; // final action
|
||||
} VpnConnectionInfoEvent;
|
||||
|
||||
typedef struct {
|
||||
@@ -523,7 +524,7 @@ typedef struct {
|
||||
/**
|
||||
* QoS class and relative priority for threads on iOS platform
|
||||
*/
|
||||
VpnQosSettings qos_settings;
|
||||
VpnQosSettings qos_settings;
|
||||
#endif // __APPLE__ && TARGET_OS_IPHONE
|
||||
} VpnSettings;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include "common/logger.h"
|
||||
#include "http_icmp_multiplexer.h"
|
||||
#include "http_udp_multiplexer.h"
|
||||
#include "net/udp_socket.h"
|
||||
#include "net/quic_connector.h"
|
||||
#include "net/udp_socket.h"
|
||||
#include "vpn/internal/data_buffer.h"
|
||||
#include "vpn/internal/server_upstream.h"
|
||||
#include "vpn/utils.h"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "tcpip/tcpip.h"
|
||||
#include "vpn/internal/client_listener.h"
|
||||
|
||||
@@ -34,10 +34,10 @@ enum ClientConnectionState {
|
||||
};
|
||||
|
||||
struct RecoveryInfo {
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_ts; // session recovery start timestamp
|
||||
std::chrono::time_point<std::chrono::steady_clock> attempt_start_ts; // last recovery attempt start timestamp
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_ts; // session recovery start timestamp
|
||||
std::chrono::time_point<std::chrono::steady_clock> attempt_start_ts; // last recovery attempt start timestamp
|
||||
Millis attempt_interval{ag::VPN_DEFAULT_INITIAL_RECOVERY_INTERVAL_MS}; // last interval between recovery attempts
|
||||
Millis to_next{}; // left to next attempt
|
||||
Millis to_next{}; // left to next attempt
|
||||
};
|
||||
|
||||
struct SelectedEndpointInfo {
|
||||
|
||||
@@ -5,6 +5,7 @@ This directory contains the new build system for VPN client and endpoint integra
|
||||
## Files
|
||||
|
||||
### Build Scripts
|
||||
|
||||
- `docker_build.sh` - Main orchestration script that handles Docker builds and image creation
|
||||
- `docker_run_tests.sh` - Test runner script (parameters: main or browser)
|
||||
- `build_client.sh` - Script to build the VPN client (runs inside Docker container)
|
||||
@@ -13,6 +14,7 @@ This directory contains the new build system for VPN client and endpoint integra
|
||||
- `endpoint_run.sh` - Script to run the VPN endpoint (saves PID to /output/vpn_endpoint.pid)
|
||||
|
||||
### Test Scripts
|
||||
|
||||
- `tests/client_setup.sh` - Script to configure the VPN client with iptables rules and create configuration (port as 4th parameter, supports custom credentials for browser tests)
|
||||
- `tests/client_run.sh` - Script to run the VPN client (saves PID to /output/vpn_client.pid)
|
||||
- `tests/main/run.sh` - Main test orchestrator (setup → run endpoint → client → test → cleanup with PID management)
|
||||
@@ -26,18 +28,21 @@ This directory contains the new build system for VPN client and endpoint integra
|
||||
## Usage
|
||||
|
||||
### Build Docker Image
|
||||
|
||||
```bash
|
||||
# Build the Docker image for testing
|
||||
./docker_build.sh image
|
||||
```
|
||||
|
||||
### Build VPN Client
|
||||
|
||||
```bash
|
||||
# CONAN_REPO_URL is required for client builds
|
||||
CONAN_REPO_URL="https://your-conan-repo.com" ./docker_build.sh client
|
||||
```
|
||||
|
||||
### Build VPN Endpoint
|
||||
|
||||
```bash
|
||||
./docker_build.sh endpoint
|
||||
```
|
||||
@@ -54,6 +59,7 @@ The build script automatically handles repository cloning:
|
||||
## Environment Variables
|
||||
|
||||
### For All Builds
|
||||
|
||||
- `VPN_LIBS_ROOT` - VPN libs source directory (default: ./vpn-libs)
|
||||
- `VPN_ENDPOINT_ROOT` - VPN endpoint source directory (default: ./vpn-libs-endpoint)
|
||||
- `OUTPUT_VOLUME` - Output directory for build artifacts (default: ./output)
|
||||
@@ -61,13 +67,16 @@ The build script automatically handles repository cloning:
|
||||
- `VPN_ENDPOINT_GIT_URL` - Git URL for VPN endpoint (default: https://github.com/TrustTunnel/TrustTunnel)
|
||||
|
||||
### For Client Builds
|
||||
|
||||
- `CONAN_REPO_URL` - Conan repository URL for dependencies (**required**)
|
||||
|
||||
### For Endpoint Setup (endpoint_setup.sh)
|
||||
|
||||
- `ENDPOINT_HOSTNAME` - Hostname for SSL certificate generation (default: endpoint.test)
|
||||
- `OUTPUT_DIR` - Directory for setup files (default: /output)
|
||||
|
||||
### For Browser Tests
|
||||
|
||||
- `BAMBOO_VPN_APP_ID` - Required for browser tests - VPN app ID for backend authentication
|
||||
- `BAMBOO_VPN_TOKEN` - Required for browser tests - VPN token for backend authentication
|
||||
- `AGVPN_HELPER_URL` - Optional URL to download agvpn_helper if not present in output directory
|
||||
@@ -75,26 +84,31 @@ The build script automatically handles repository cloning:
|
||||
## Examples
|
||||
|
||||
### Build Docker image
|
||||
|
||||
```bash
|
||||
./docker_build.sh image
|
||||
```
|
||||
|
||||
### Build client with custom Conan repository
|
||||
|
||||
```bash
|
||||
CONAN_REPO_URL="https://your-conan-repo.com" ./docker_build.sh client
|
||||
```
|
||||
|
||||
### Build endpoint (hostname is set during setup phase)
|
||||
|
||||
```bash
|
||||
./docker_build.sh endpoint
|
||||
```
|
||||
|
||||
### Build with custom output directory
|
||||
|
||||
```bash
|
||||
OUTPUT_VOLUME="/tmp/build-output" ./docker_build.sh client
|
||||
```
|
||||
|
||||
### Build with custom Git repositories
|
||||
|
||||
```bash
|
||||
VPN_LIBS_GIT_URL="https://github.com/yourfork/TrustTunnelClient" ./docker_build.sh client
|
||||
VPN_ENDPOINT_GIT_URL="https://github.com/yourfork/TrustTunnel" ./docker_build.sh endpoint
|
||||
@@ -109,6 +123,7 @@ For the endpoint, the build process is now separated into distinct phases:
|
||||
3. **Run**: `./endpoint_run.sh` - Starts the VPN endpoint server
|
||||
|
||||
### Running endpoint setup and execution separately
|
||||
|
||||
```bash
|
||||
# Build the endpoint
|
||||
./docker_build.sh endpoint
|
||||
@@ -125,13 +140,16 @@ OUTPUT_DIR="./output" LOG_LEVEL="debug" ./endpoint_run.sh
|
||||
The test system provides automated test execution with endpoint management:
|
||||
|
||||
### Test Types
|
||||
|
||||
- **Main tests**: `./docker_run_tests.sh main`
|
||||
- **Browser tests**: `./docker_run_tests.sh browser`
|
||||
|
||||
### Test Workflow
|
||||
|
||||
#### Main Tests
|
||||
|
||||
Each main test run automatically:
|
||||
|
||||
1. Sets up the VPN endpoint (certificates, configuration)
|
||||
2. Starts the endpoint in the background (saves PID to `/output/vpn_endpoint.pid`)
|
||||
3. Determines endpoint IP addresses
|
||||
@@ -144,7 +162,9 @@ Each main test run automatically:
|
||||
7. Stops processes using PID files and cleans up (including network namespace)
|
||||
|
||||
#### Browser Tests
|
||||
|
||||
Each browser test run automatically:
|
||||
|
||||
1. Downloads `agvpn_helper` if not present (using `AGVPN_HELPER_URL` if provided)
|
||||
2. Fetches real backend location and credentials using `agvpn_helper`
|
||||
3. Sets up the VPN client with real backend configuration
|
||||
@@ -160,7 +180,9 @@ Each browser test run automatically:
|
||||
**Note**: The test container has access to built binaries (`trusttunnel_client`, `trusttunnel_endpoint`) via the mounted `/output` directory.
|
||||
|
||||
### Test Parameters
|
||||
|
||||
The test runners (`tests/main/run.sh` and `tests/browser/run.sh`) accept optional parameters:
|
||||
|
||||
- `protocol` - Protocol to test (default: https)
|
||||
- `mode` - Test mode: tun or socks (default: tun)
|
||||
- `socks_port` - SOCKS port when mode=socks (default: 7777)
|
||||
@@ -169,6 +191,7 @@ The test runners (`tests/main/run.sh` and `tests/browser/run.sh`) accept optiona
|
||||
These parameters are automatically passed to the underlying `run_tests.sh` scripts along with endpoint connection details.
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Run main tests
|
||||
./docker_run_tests.sh main
|
||||
@@ -188,13 +211,16 @@ ENDPOINT_HOSTNAME="test.local" ./docker_run_tests.sh main
|
||||
Build artifacts will be placed in the `output` directory (or the directory specified by `OUTPUT_VOLUME`):
|
||||
|
||||
### Client Build Output
|
||||
|
||||
- `trusttunnel_client` - The built VPN client executable
|
||||
- Additional test scripts (if present in source)
|
||||
|
||||
### Endpoint Build Output
|
||||
|
||||
- `trusttunnel_endpoint` - The built VPN endpoint executable (from `build_endpoint.sh`)
|
||||
|
||||
### Endpoint Setup Output (from `endpoint_setup.sh`)
|
||||
|
||||
- `cert.pem` - SSL certificate
|
||||
- `key.pem` - SSL private key
|
||||
- `vpn.conf` - VPN configuration file
|
||||
@@ -228,6 +254,7 @@ The test framework uses PID files for robust process lifecycle management:
|
||||
The browser tests provide comprehensive network load simulation:
|
||||
|
||||
### Features
|
||||
|
||||
- **Puppeteer-based**: Uses headless Chrome to simulate real browser traffic
|
||||
- **Multiple URLs**: Tests against BBC, Google, Guardian, and AdGuard websites
|
||||
- **Network Disruption**: Simulates network problems and tests VPN reconnection
|
||||
@@ -236,6 +263,7 @@ The browser tests provide comprehensive network load simulation:
|
||||
- **Real Backend**: Uses `agvpn_helper` to connect to actual VPN backend infrastructure
|
||||
|
||||
### Browser Test Output
|
||||
|
||||
- `output1part.json` - Test results from the first 30-minute phase
|
||||
- `output2part.json` - Test results after network disruption and recovery
|
||||
- Detailed statistics including request timing, error counts, and reload frequencies
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "net/network_manager.h"
|
||||
#include "net/utils.h"
|
||||
#include "vpn/event_loop.h"
|
||||
#include "net/network_manager.h"
|
||||
|
||||
namespace ag {
|
||||
|
||||
@@ -32,24 +32,24 @@ typedef struct {
|
||||
#ifdef __MACH__
|
||||
bool query_all_interfaces; // Query all interfaces to calculate pings. Supported only on Apple platforms.
|
||||
#endif
|
||||
VpnUpstreamProtocol main_protocol; // Application-level protocol override for VPN endpoint protocols.
|
||||
// @see `VpnUpstreamConfig.main_protocol` for full description.
|
||||
bool anti_dpi; // Enable anti-DPI measures.
|
||||
bool handoff; // For internal use. Applications should set this parameter to `false`.
|
||||
// If `true`, pass the connection state with the ping result.
|
||||
const VpnRelay *relay_parallel; // Ping through this relay in parallel with normal pings.
|
||||
uint32_t quic_max_idle_timeout_ms; // QUIC connection max idle timeout. Set `0` to use the default.
|
||||
uint32_t quic_version; // QUIC version. Set `0` to use the default.
|
||||
VpnUpstreamProtocol main_protocol; // Application-level protocol override for VPN endpoint protocols.
|
||||
// @see `VpnUpstreamConfig.main_protocol` for full description.
|
||||
bool anti_dpi; // Enable anti-DPI measures.
|
||||
bool handoff; // For internal use. Applications should set this parameter to `false`.
|
||||
// If `true`, pass the connection state with the ping result.
|
||||
const VpnRelay *relay_parallel; // Ping through this relay in parallel with normal pings.
|
||||
uint32_t quic_max_idle_timeout_ms; // QUIC connection max idle timeout. Set `0` to use the default.
|
||||
uint32_t quic_version; // QUIC version. Set `0` to use the default.
|
||||
} LocationsPingerInfo;
|
||||
|
||||
typedef struct {
|
||||
const char *id; // location id
|
||||
int ping_ms; // selected endpoint's ping (negative if none of the location endpoints successfully pinged)
|
||||
const VpnEndpoint *endpoint; // selected endpoint
|
||||
const VpnRelay *relay; // non-null if the selected endpoint was pinged through a relay
|
||||
bool is_quic; // Whether the established connection is QUIC
|
||||
void *conn_state; // For internal use. Applications should ignore this field.
|
||||
// If `handoff` is `true`, this is the connection state object.
|
||||
const VpnEndpoint *endpoint; // selected endpoint
|
||||
const VpnRelay *relay; // non-null if the selected endpoint was pinged through a relay
|
||||
bool is_quic; // Whether the established connection is QUIC
|
||||
void *conn_state; // For internal use. Applications should ignore this field.
|
||||
// If `handoff` is `true`, this is the connection state object.
|
||||
} LocationsPingerResult;
|
||||
|
||||
typedef struct {
|
||||
@@ -70,8 +70,8 @@ typedef struct {
|
||||
* @param network_manager network manager
|
||||
* @return pinger context
|
||||
*/
|
||||
LocationsPinger *locations_pinger_start(
|
||||
const LocationsPingerInfo *info, LocationsPingerHandler handler, VpnEventLoop *ev_loop, VpnNetworkManager *network_manager);
|
||||
LocationsPinger *locations_pinger_start(const LocationsPingerInfo *info, LocationsPingerHandler handler,
|
||||
VpnEventLoop *ev_loop, VpnNetworkManager *network_manager);
|
||||
|
||||
/**
|
||||
* Stop pinging
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace ag {
|
||||
|
||||
@@ -15,6 +15,7 @@ using VpnMacDnsSettingsManagerImplPtr = std::unique_ptr<VpnMacDnsSettingsManager
|
||||
class VpnMacDnsSettingsManager {
|
||||
VpnMacDnsSettingsManagerImplPtr m_pimpl;
|
||||
struct ConstructorAccess {};
|
||||
|
||||
public:
|
||||
VpnMacDnsSettingsManager(ConstructorAccess access, std::span<const std::string_view> dns_servers);
|
||||
~VpnMacDnsSettingsManager();
|
||||
|
||||
@@ -260,7 +260,8 @@ void fsystem(std::string_view fmt, Ts &&...args) { // NOLINT(*-missing-std-forwa
|
||||
}
|
||||
Result<std::string, ExecError> sys_cmd_with_output(std::string cmd);
|
||||
template <typename... Ts>
|
||||
Result<std::string, ExecError> fsystem_with_output(std::string_view fmt, Ts &&...args) { // NOLINT(*-missing-std-forward)
|
||||
Result<std::string, ExecError> fsystem_with_output(
|
||||
std::string_view fmt, Ts &&...args) { // NOLINT(*-missing-std-forward)
|
||||
return sys_cmd_with_output(fmt::vformat(fmt, fmt::make_format_args(args...)).c_str());
|
||||
}
|
||||
#endif // defined _WIN32
|
||||
|
||||
@@ -32,8 +32,8 @@ enum Socks5ListenerEvent {
|
||||
SOCKS5L_EVENT_DATA_SENT, /**< Called when some data was sent to client (raised with `socks5l_data_sent_event_t`) */
|
||||
SOCKS5L_EVENT_CONNECTION_CLOSED, /**< Called when connection is closed by client (raised with
|
||||
`socks5l_connection_closed_event_t`) */
|
||||
SOCKS5L_EVENT_PROTECT_SOCKET, /**< Called when socket needs to be protected (raised with `SocketProtectEvent`)
|
||||
*/
|
||||
SOCKS5L_EVENT_PROTECT_SOCKET, /**< Called when socket needs to be protected (raised with `SocketProtectEvent`)
|
||||
*/
|
||||
};
|
||||
|
||||
enum Socks5ConnectionAddressType {
|
||||
|
||||
@@ -61,8 +61,8 @@ typedef struct {
|
||||
SSL *ssl; // SSL context in case of the traffic needs to be encrypted
|
||||
bool anti_dpi; // Enable anti-DPI protection
|
||||
bool pause_tls; // Pause the TLS handshake and raise `TCP_SOCKET_EVENT_CONNECTED` after receiving the
|
||||
// first bytes from server. Continue the handshake by calling `tcp_socket_connect_continue`.
|
||||
// `TCP_SOCKET_EVENT_CONNECTED` will be raised one more time when the handshake is complete.
|
||||
// first bytes from server. Continue the handshake by calling `tcp_socket_connect_continue`.
|
||||
// `TCP_SOCKET_EVENT_CONNECTED` will be raised one more time when the handshake is complete.
|
||||
} TcpSocketConnectParameters;
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,7 +100,7 @@ bool tls_verify_cert_ip(X509 *cert, const char *ip);
|
||||
* @param store trusted CA store (if NULL, `tls_create_ca_store` will be used)
|
||||
* @return NULL if verified successfully, error message otherwise
|
||||
*/
|
||||
const char *tls_verify_cert(X509 *cert, STACK_OF(X509) *chain, X509_STORE *store);
|
||||
const char *tls_verify_cert(X509 *cert, STACK_OF(X509) * chain, X509_STORE *store);
|
||||
|
||||
/**
|
||||
* Create trusted CA store from system's one
|
||||
@@ -112,8 +112,7 @@ X509_STORE *tls_create_ca_store();
|
||||
#define tls_input(t, d, s) (t)->in = {(uint8_t *) (d), size_t(s)}
|
||||
|
||||
/** Setup to parse a handshake record. */
|
||||
#define tls_input_hshake(t, d, s) \
|
||||
(t)->rec = {(uint8_t *) (d), size_t(s)}, (t)->state = 1
|
||||
#define tls_input_hshake(t, d, s) (t)->rec = {(uint8_t *) (d), size_t(s)}, (t)->state = 1
|
||||
|
||||
/**
|
||||
* Parse TLS record
|
||||
|
||||
@@ -14,6 +14,7 @@ bool hkdf_extract(std::span<uint8_t> dest, std::span<const uint8_t> secret, std:
|
||||
/**
|
||||
* HKDF-Expand-Label is wrapper around HKDF-Expand(secret, info) which builds info from several input parameters
|
||||
*/
|
||||
bool hkdf_expand_label(std::span<uint8_t> dest, std::span<const uint8_t> secret, std::string_view label, std::span<const uint8_t> context = {});
|
||||
bool hkdf_expand_label(std::span<uint8_t> dest, std::span<const uint8_t> secret, std::string_view label,
|
||||
std::span<const uint8_t> context = {});
|
||||
|
||||
}
|
||||
} // namespace ag::tls13_utils
|
||||
|
||||
+15
-16
@@ -15,11 +15,11 @@
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include "common/error.h"
|
||||
#include <common/net_utils.h>
|
||||
#include "common/socket_address.h"
|
||||
#include "common/utils.h"
|
||||
#include "net/http_header.h"
|
||||
#include "vpn/utils.h"
|
||||
#include <common/net_utils.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
namespace ag {
|
||||
@@ -48,22 +48,22 @@ struct CertVerifyHandler {
|
||||
|
||||
struct VpnEndpoint {
|
||||
SocketAddressStorage address; // endpoint address
|
||||
const char *name; // endpoint host name (used, for example, for TLS handshake)
|
||||
const char *remote_id; // if not NULL or empty, used for server TLS certificate verification instead of `name`
|
||||
AG_ARRAY_OF(uint8_t) additional_data; // additional data about the endpoint
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random; // custom client random
|
||||
const char *name; // endpoint host name (used, for example, for TLS handshake)
|
||||
const char *remote_id; // if not NULL or empty, used for server TLS certificate verification instead of `name`
|
||||
AG_ARRAY_OF(uint8_t) additional_data; // additional data about the endpoint
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random; // custom client random
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random_mask; // mask for custom client random
|
||||
bool has_ipv6; // Whether IPv6 traffic can be routed through the endpoint
|
||||
VpnUpstreamProtocol preferred_protocol; // Protocol to use for the endpoint connection.
|
||||
// @see `VpnUpstreamConfig.main_protocol` for full description.
|
||||
bool has_ipv6; // Whether IPv6 traffic can be routed through the endpoint
|
||||
VpnUpstreamProtocol preferred_protocol; // Protocol to use for the endpoint connection.
|
||||
// @see `VpnUpstreamConfig.main_protocol` for full description.
|
||||
};
|
||||
|
||||
typedef AG_ARRAY_OF(VpnEndpoint) VpnEndpoints;
|
||||
|
||||
struct VpnRelay {
|
||||
SocketAddressStorage address; // relay address
|
||||
AG_ARRAY_OF(uint8_t) additional_data; // additional data about the relay
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random; // custom client random
|
||||
SocketAddressStorage address; // relay address
|
||||
AG_ARRAY_OF(uint8_t) additional_data; // additional data about the relay
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random; // custom client random
|
||||
AG_ARRAY_OF(uint8_t) tls_client_random_mask; // mask for custom client random
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ typedef AG_ARRAY_OF(VpnRelay) VpnRelays;
|
||||
struct VpnLocation {
|
||||
const char *id; // location id
|
||||
VpnEndpoints endpoints; // location endpoints
|
||||
VpnRelays relays; // location relays
|
||||
VpnRelays relays; // location relays
|
||||
};
|
||||
|
||||
struct NameValue {
|
||||
@@ -122,7 +122,7 @@ struct IcmpEchoRequest {
|
||||
|
||||
struct IcmpEchoReply {
|
||||
/** source address of the reply (essentially equals to `dst` in corresponding `tcpip_icmp_echo_t`) */
|
||||
SocketAddress peer;
|
||||
SocketAddress peer;
|
||||
uint16_t id; /**< an identifier to aid in matching echos and replies */
|
||||
uint16_t seqno; /**< a sequence number to aid in matching echos and replies */
|
||||
uint8_t type; /**< a type of the reply message */
|
||||
@@ -165,7 +165,7 @@ constexpr std::string_view AG_UNFILTERED_DNS_IPS_V6[] = {
|
||||
};
|
||||
|
||||
constexpr auto DPI_COOLDOWN_TIME = Millis{25}; // Time after the first part of the ClientHello is sent
|
||||
constexpr size_t DPI_SPLIT_SIZE = 1; // Size of the first part of the ClientHello
|
||||
constexpr size_t DPI_SPLIT_SIZE = 1; // Size of the first part of the ClientHello
|
||||
|
||||
/**
|
||||
* Serializes HTTP headers structure to valid HTTP/1.1 message (request or response)
|
||||
@@ -393,8 +393,7 @@ struct fmt::formatter<ag::VpnEndpoint> {
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const ag::VpnEndpoint &endpoint, FormatContext &ctx) {
|
||||
return fmt::format_to(
|
||||
ctx.out(), "name={}, address={}", endpoint.name, ag::SocketAddress(endpoint.address));
|
||||
return fmt::format_to(ctx.out(), "name={}, address={}", endpoint.name, ag::SocketAddress(endpoint.address));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+10
-10
@@ -2,9 +2,9 @@
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "net/network_manager.h"
|
||||
#include "net/utils.h"
|
||||
#include "vpn/event_loop.h"
|
||||
#include "net/network_manager.h"
|
||||
|
||||
namespace ag {
|
||||
|
||||
@@ -18,20 +18,20 @@ enum PingStatus {
|
||||
};
|
||||
|
||||
struct PingResult {
|
||||
Ping *ping; // ping pointer (don't delete from callback unless PING_FINISHED is reported)
|
||||
PingStatus status; // ping status
|
||||
int socket_error; // has sense if `status` == `PING_SOCKET_ERROR`
|
||||
const VpnEndpoint *endpoint; // pinged endpoint
|
||||
int ms; // RTT value
|
||||
const VpnRelay *relay; // non-null if the endpoint was pinged through a relay
|
||||
bool is_quic; // Whether the established connection is QUIC
|
||||
void *conn_state; // Connection object, non-NULL if connection hand-off is enabled
|
||||
Ping *ping; // ping pointer (don't delete from callback unless PING_FINISHED is reported)
|
||||
PingStatus status; // ping status
|
||||
int socket_error; // has sense if `status` == `PING_SOCKET_ERROR`
|
||||
const VpnEndpoint *endpoint; // pinged endpoint
|
||||
int ms; // RTT value
|
||||
const VpnRelay *relay; // non-null if the endpoint was pinged through a relay
|
||||
bool is_quic; // Whether the established connection is QUIC
|
||||
void *conn_state; // Connection object, non-NULL if connection hand-off is enabled
|
||||
};
|
||||
|
||||
struct PingInfo {
|
||||
const char *id = ""; ///< An ID string for correlating log messages
|
||||
|
||||
VpnEventLoop *loop = nullptr; ///< Event loop
|
||||
VpnEventLoop *loop = nullptr; ///< Event loop
|
||||
VpnNetworkManager *network_manager = nullptr;
|
||||
std::span<const VpnEndpoint> endpoints; ///< List of endpoints to ping
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <p align="center">TrustTunnel Client Android adapter</p>
|
||||
# TrustTunnel Client Android adapter
|
||||
|
||||
## Build Instructions
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
You can also download SDK and NDK using Android Studio SDK Manager.
|
||||
|
||||
|
||||
### Building
|
||||
|
||||
To obtain all required conan dependencies, go to the root of the project and run:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <jni.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/**
|
||||
* Attaches the current thread, if necessary, and pushes a local reference frame. Reverses that in dtor.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Launch Screen Assets
|
||||
|
||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||
You can customize the launch screen with your own desired assets by replacing
|
||||
the image files in this directory.
|
||||
|
||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
||||
You can also do it by opening your Flutter project's Xcode project with
|
||||
`open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project
|
||||
Navigator and dropping in the desired images.
|
||||
|
||||
@@ -7,33 +7,32 @@
|
||||
#include <memory>
|
||||
|
||||
#include "pigeon/native_communication.h"
|
||||
#include "win32_window.h"
|
||||
#include "ui_thread_dispatcher.h"
|
||||
#include "win32_window.h"
|
||||
|
||||
// A window that does nothing but host a Flutter view.
|
||||
class FlutterWindow : public Win32Window, public IUIThreadDispatcher {
|
||||
public:
|
||||
// Creates a new FlutterWindow hosting a Flutter view running |project|.
|
||||
explicit FlutterWindow(const flutter::DartProject& project);
|
||||
virtual ~FlutterWindow();
|
||||
public:
|
||||
// Creates a new FlutterWindow hosting a Flutter view running |project|.
|
||||
explicit FlutterWindow(const flutter::DartProject &project);
|
||||
virtual ~FlutterWindow();
|
||||
|
||||
void RunOnUIThread(std::function<void()> task) override;
|
||||
void RunOnUIThread(std::function<void()> task) override;
|
||||
|
||||
protected:
|
||||
// Win32Window:
|
||||
bool OnCreate() override;
|
||||
void OnDestroy() override;
|
||||
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept override;
|
||||
protected:
|
||||
// Win32Window:
|
||||
bool OnCreate() override;
|
||||
void OnDestroy() override;
|
||||
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
|
||||
|
||||
private:
|
||||
// The project to run.
|
||||
flutter::DartProject project_;
|
||||
private:
|
||||
// The project to run.
|
||||
flutter::DartProject project_;
|
||||
|
||||
// The Flutter instance hosted by this window.
|
||||
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||
// The Flutter instance hosted by this window.
|
||||
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||
|
||||
std::unique_ptr<NativeVpnInterface> native_interface_;
|
||||
std::unique_ptr<NativeVpnInterface> native_interface_;
|
||||
};
|
||||
|
||||
#endif // RUNNER_FLUTTER_WINDOW_H_
|
||||
#endif // RUNNER_FLUTTER_WINDOW_H_
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
class NativeVpnImpl : public NativeVpnInterface {
|
||||
public:
|
||||
NativeVpnImpl(IUIThreadDispatcher *dispatcher, FlutterCallbacks &&callbacks);
|
||||
std::optional<FlutterError> Start(const std::string& serverName, const std::string &config) override;
|
||||
std::optional<FlutterError> Start(const std::string &serverName, const std::string &config) override;
|
||||
std::optional<FlutterError> Stop() override;
|
||||
void NotifyStateChanged(int state);
|
||||
|
||||
private:
|
||||
ag::Logger m_logger{"NativeVpnImpl"};
|
||||
|
||||
|
||||
@@ -12,113 +12,126 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
// Generated class from Pigeon.
|
||||
|
||||
class FlutterError {
|
||||
public:
|
||||
explicit FlutterError(const std::string& code)
|
||||
: code_(code) {}
|
||||
explicit FlutterError(const std::string& code, const std::string& message)
|
||||
: code_(code), message_(message) {}
|
||||
explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details)
|
||||
: code_(code), message_(message), details_(details) {}
|
||||
public:
|
||||
explicit FlutterError(const std::string &code)
|
||||
: code_(code) {
|
||||
}
|
||||
explicit FlutterError(const std::string &code, const std::string &message)
|
||||
: code_(code)
|
||||
, message_(message) {
|
||||
}
|
||||
explicit FlutterError(const std::string &code, const std::string &message, const flutter::EncodableValue &details)
|
||||
: code_(code)
|
||||
, message_(message)
|
||||
, details_(details) {
|
||||
}
|
||||
|
||||
const std::string& code() const { return code_; }
|
||||
const std::string& message() const { return message_; }
|
||||
const flutter::EncodableValue& details() const { return details_; }
|
||||
const std::string &code() const {
|
||||
return code_;
|
||||
}
|
||||
const std::string &message() const {
|
||||
return message_;
|
||||
}
|
||||
const flutter::EncodableValue &details() const {
|
||||
return details_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string code_;
|
||||
std::string message_;
|
||||
flutter::EncodableValue details_;
|
||||
private:
|
||||
std::string code_;
|
||||
std::string message_;
|
||||
flutter::EncodableValue details_;
|
||||
};
|
||||
|
||||
template<class T> class ErrorOr {
|
||||
public:
|
||||
ErrorOr(const T& rhs) : v_(rhs) {}
|
||||
ErrorOr(const T&& rhs) : v_(std::move(rhs)) {}
|
||||
ErrorOr(const FlutterError& rhs) : v_(rhs) {}
|
||||
ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {}
|
||||
template <class T>
|
||||
class ErrorOr {
|
||||
public:
|
||||
ErrorOr(const T &rhs)
|
||||
: v_(rhs) {
|
||||
}
|
||||
ErrorOr(const T &&rhs)
|
||||
: v_(std::move(rhs)) {
|
||||
}
|
||||
ErrorOr(const FlutterError &rhs)
|
||||
: v_(rhs) {
|
||||
}
|
||||
ErrorOr(const FlutterError &&rhs)
|
||||
: v_(std::move(rhs)) {
|
||||
}
|
||||
|
||||
bool has_error() const { return std::holds_alternative<FlutterError>(v_); }
|
||||
const T& value() const { return std::get<T>(v_); };
|
||||
const FlutterError& error() const { return std::get<FlutterError>(v_); };
|
||||
bool has_error() const {
|
||||
return std::holds_alternative<FlutterError>(v_);
|
||||
}
|
||||
const T &value() const {
|
||||
return std::get<T>(v_);
|
||||
};
|
||||
const FlutterError &error() const {
|
||||
return std::get<FlutterError>(v_);
|
||||
};
|
||||
|
||||
private:
|
||||
friend class NativeVpnInterface;
|
||||
friend class FlutterCallbacks;
|
||||
ErrorOr() = default;
|
||||
T TakeValue() && { return std::get<T>(std::move(v_)); }
|
||||
private:
|
||||
friend class NativeVpnInterface;
|
||||
friend class FlutterCallbacks;
|
||||
ErrorOr() = default;
|
||||
T TakeValue() && {
|
||||
return std::get<T>(std::move(v_));
|
||||
}
|
||||
|
||||
std::variant<T, FlutterError> v_;
|
||||
std::variant<T, FlutterError> v_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer {
|
||||
public:
|
||||
PigeonInternalCodecSerializer();
|
||||
inline static PigeonInternalCodecSerializer& GetInstance() {
|
||||
static PigeonInternalCodecSerializer sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
public:
|
||||
PigeonInternalCodecSerializer();
|
||||
inline static PigeonInternalCodecSerializer &GetInstance() {
|
||||
static PigeonInternalCodecSerializer sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void WriteValue(
|
||||
const flutter::EncodableValue& value,
|
||||
flutter::ByteStreamWriter* stream) const override;
|
||||
protected:
|
||||
flutter::EncodableValue ReadValueOfType(
|
||||
uint8_t type,
|
||||
flutter::ByteStreamReader* stream) const override;
|
||||
void WriteValue(const flutter::EncodableValue &value, flutter::ByteStreamWriter *stream) const override;
|
||||
|
||||
protected:
|
||||
flutter::EncodableValue ReadValueOfType(uint8_t type, flutter::ByteStreamReader *stream) const override;
|
||||
};
|
||||
|
||||
// Generated interface from Pigeon that represents a handler of messages from Flutter.
|
||||
class NativeVpnInterface {
|
||||
public:
|
||||
NativeVpnInterface(const NativeVpnInterface&) = delete;
|
||||
NativeVpnInterface& operator=(const NativeVpnInterface&) = delete;
|
||||
virtual ~NativeVpnInterface() {}
|
||||
virtual std::optional<FlutterError> Start(
|
||||
const std::string& server_name,
|
||||
const std::string& config) = 0;
|
||||
virtual std::optional<FlutterError> Stop() = 0;
|
||||
public:
|
||||
NativeVpnInterface(const NativeVpnInterface &) = delete;
|
||||
NativeVpnInterface &operator=(const NativeVpnInterface &) = delete;
|
||||
virtual ~NativeVpnInterface() {
|
||||
}
|
||||
virtual std::optional<FlutterError> Start(const std::string &server_name, const std::string &config) = 0;
|
||||
virtual std::optional<FlutterError> Stop() = 0;
|
||||
|
||||
// The codec used by NativeVpnInterface.
|
||||
static const flutter::StandardMessageCodec& GetCodec();
|
||||
// Sets up an instance of `NativeVpnInterface` to handle messages through the `binary_messenger`.
|
||||
static void SetUp(
|
||||
flutter::BinaryMessenger* binary_messenger,
|
||||
NativeVpnInterface* api);
|
||||
static void SetUp(
|
||||
flutter::BinaryMessenger* binary_messenger,
|
||||
NativeVpnInterface* api,
|
||||
const std::string& message_channel_suffix);
|
||||
static flutter::EncodableValue WrapError(std::string_view error_message);
|
||||
static flutter::EncodableValue WrapError(const FlutterError& error);
|
||||
protected:
|
||||
NativeVpnInterface() = default;
|
||||
// The codec used by NativeVpnInterface.
|
||||
static const flutter::StandardMessageCodec &GetCodec();
|
||||
// Sets up an instance of `NativeVpnInterface` to handle messages through the `binary_messenger`.
|
||||
static void SetUp(flutter::BinaryMessenger *binary_messenger, NativeVpnInterface *api);
|
||||
static void SetUp(flutter::BinaryMessenger *binary_messenger, NativeVpnInterface *api,
|
||||
const std::string &message_channel_suffix);
|
||||
static flutter::EncodableValue WrapError(std::string_view error_message);
|
||||
static flutter::EncodableValue WrapError(const FlutterError &error);
|
||||
|
||||
protected:
|
||||
NativeVpnInterface() = default;
|
||||
};
|
||||
// Generated class from Pigeon that represents Flutter messages that can be called from C++.
|
||||
class FlutterCallbacks {
|
||||
public:
|
||||
FlutterCallbacks(flutter::BinaryMessenger* binary_messenger);
|
||||
FlutterCallbacks(
|
||||
flutter::BinaryMessenger* binary_messenger,
|
||||
const std::string& message_channel_suffix);
|
||||
static const flutter::StandardMessageCodec& GetCodec();
|
||||
void OnStateChanged(
|
||||
int64_t state,
|
||||
std::function<void(void)>&& on_success,
|
||||
std::function<void(const FlutterError&)>&& on_error);
|
||||
void OnConnectionInfo(
|
||||
const std::string& info,
|
||||
std::function<void(void)>&& on_success,
|
||||
std::function<void(const FlutterError&)>&& on_error);
|
||||
private:
|
||||
flutter::BinaryMessenger* binary_messenger_;
|
||||
std::string message_channel_suffix_;
|
||||
public:
|
||||
FlutterCallbacks(flutter::BinaryMessenger *binary_messenger);
|
||||
FlutterCallbacks(flutter::BinaryMessenger *binary_messenger, const std::string &message_channel_suffix);
|
||||
static const flutter::StandardMessageCodec &GetCodec();
|
||||
void OnStateChanged(int64_t state, std::function<void(void)> &&on_success,
|
||||
std::function<void(const FlutterError &)> &&on_error);
|
||||
void OnConnectionInfo(const std::string &info, std::function<void(void)> &&on_success,
|
||||
std::function<void(const FlutterError &)> &&on_error);
|
||||
|
||||
private:
|
||||
flutter::BinaryMessenger *binary_messenger_;
|
||||
std::string message_channel_suffix_;
|
||||
};
|
||||
|
||||
#endif // PIGEON_NATIVE_COMMUNICATION_H_
|
||||
#endif // PIGEON_NATIVE_COMMUNICATION_H_
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Runner.rc
|
||||
//
|
||||
#define IDI_APP_ICON 101
|
||||
#define IDI_APP_ICON 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -10,10 +10,10 @@ void CreateAndAttachConsole();
|
||||
|
||||
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
|
||||
// encoded in UTF-8. Returns an empty std::string on failure.
|
||||
std::string Utf8FromUtf16(const wchar_t* utf16_string);
|
||||
std::string Utf8FromUtf16(const wchar_t *utf16_string);
|
||||
|
||||
// Gets the command line arguments passed in as a std::vector<std::string>,
|
||||
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
|
||||
std::vector<std::string> GetCommandLineArguments();
|
||||
|
||||
#endif // RUNNER_UTILS_H_
|
||||
#endif // RUNNER_UTILS_H_
|
||||
|
||||
@@ -11,92 +11,92 @@
|
||||
// inherited from by classes that wish to specialize with custom
|
||||
// rendering and input handling
|
||||
class Win32Window {
|
||||
public:
|
||||
struct Point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||
};
|
||||
public:
|
||||
struct Point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y)
|
||||
: x(x)
|
||||
, y(y) {
|
||||
}
|
||||
};
|
||||
|
||||
struct Size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
: width(width), height(height) {}
|
||||
};
|
||||
struct Size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
: width(width)
|
||||
, height(height) {
|
||||
}
|
||||
};
|
||||
|
||||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
|
||||
// Creates a win32 window with |title| that is positioned and sized using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size this function will scale the inputted width and height as
|
||||
// as appropriate for the default monitor. The window is invisible until
|
||||
// |Show| is called. Returns true if the window was created successfully.
|
||||
bool Create(const std::wstring& title, const Point& origin, const Size& size);
|
||||
// Creates a win32 window with |title| that is positioned and sized using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size this function will scale the inputted width and height as
|
||||
// as appropriate for the default monitor. The window is invisible until
|
||||
// |Show| is called. Returns true if the window was created successfully.
|
||||
bool Create(const std::wstring &title, const Point &origin, const Size &size);
|
||||
|
||||
// Show the current window. Returns true if the window was successfully shown.
|
||||
bool Show();
|
||||
// Show the current window. Returns true if the window was successfully shown.
|
||||
bool Show();
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
|
||||
// Inserts |content| into the window tree.
|
||||
void SetChildContent(HWND content);
|
||||
// Inserts |content| into the window tree.
|
||||
void SetChildContent(HWND content);
|
||||
|
||||
// Returns the backing Window handle to enable clients to set icon and other
|
||||
// window properties. Returns nullptr if the window has been destroyed.
|
||||
HWND GetHandle();
|
||||
// Returns the backing Window handle to enable clients to set icon and other
|
||||
// window properties. Returns nullptr if the window has been destroyed.
|
||||
HWND GetHandle();
|
||||
|
||||
// If true, closing this window will quit the application.
|
||||
void SetQuitOnClose(bool quit_on_close);
|
||||
// If true, closing this window will quit the application.
|
||||
void SetQuitOnClose(bool quit_on_close);
|
||||
|
||||
// Return a RECT representing the bounds of the current client area.
|
||||
RECT GetClientArea();
|
||||
// Return a RECT representing the bounds of the current client area.
|
||||
RECT GetClientArea();
|
||||
|
||||
protected:
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
virtual LRESULT MessageHandler(HWND window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
protected:
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
virtual LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
|
||||
|
||||
// Called when CreateAndShow is called, allowing subclass window-related
|
||||
// setup. Subclasses should return false if setup fails.
|
||||
virtual bool OnCreate();
|
||||
// Called when CreateAndShow is called, allowing subclass window-related
|
||||
// setup. Subclasses should return false if setup fails.
|
||||
virtual bool OnCreate();
|
||||
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
// is passed when the non-client area is being created and enables automatic
|
||||
// non-client DPI scaling so that the non-client area automatically
|
||||
// responds to changes in DPI. All other messages are handled by
|
||||
// MessageHandler.
|
||||
static LRESULT CALLBACK WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
// is passed when the non-client area is being created and enables automatic
|
||||
// non-client DPI scaling so that the non-client area automatically
|
||||
// responds to changes in DPI. All other messages are handled by
|
||||
// MessageHandler.
|
||||
static LRESULT CALLBACK WndProc(
|
||||
HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
|
||||
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window *GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
// Update the window frame's theme to match the system theme.
|
||||
static void UpdateTheme(HWND const window);
|
||||
// Update the window frame's theme to match the system theme.
|
||||
static void UpdateTheme(HWND const window);
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
// window handle for top level window.
|
||||
HWND window_handle_ = nullptr;
|
||||
// window handle for top level window.
|
||||
HWND window_handle_ = nullptr;
|
||||
|
||||
// window handle for hosted content.
|
||||
HWND child_content_ = nullptr;
|
||||
// window handle for hosted content.
|
||||
HWND child_content_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Easy wrapper for AdGuard VPI API
|
||||
|
||||
- Basically, the `trusttunnel_client` command line application in the form of a library.
|
||||
- Only two buttons: `start` and `stop`. The first one accepts the configuration in TOML format.
|
||||
- For tunnel listener to work, `wintun.dll` (architecture matching the `vpn_easy` binary)
|
||||
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e -f -u
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 1
|
||||
|
||||
# Only show interactive prompts if there a terminal is attached to stdout.
|
||||
# While this technically doesn't guarantee that reading from /dev/tty works,
|
||||
# this should work reasonably well on all of our supported development systems
|
||||
# and in most terminal emulators.
|
||||
is_tty='0'
|
||||
if [ -t '1' ]; then
|
||||
is_tty='1'
|
||||
fi
|
||||
readonly is_tty
|
||||
|
||||
# prompt is a helper that prompts the user for interactive input if that can be
|
||||
# done. If there is no terminal attached, it sleeps for two seconds, giving the
|
||||
# programmer some time to react, and returns with a zero exit code.
|
||||
prompt() {
|
||||
if [ "$is_tty" -eq '0' ]; then
|
||||
sleep 2
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
while true; do
|
||||
printf 'commit anyway? y/[n]: '
|
||||
read -r ans </dev/tty
|
||||
|
||||
case "$ans" in
|
||||
'y' | 'Y')
|
||||
break
|
||||
;;
|
||||
'' | 'n' | 'N')
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Warn the programmer about unstaged changes and untracked files, but do not
|
||||
# fail the commit, because those changes might be temporary or for a different
|
||||
# branch.
|
||||
#
|
||||
# shellcheck disable=SC2016
|
||||
awk_prog='substr($2, 2, 1) != "." { print $9; } $1 == "?" { print $2; }'
|
||||
readonly awk_prog
|
||||
|
||||
unstaged="$(git status --porcelain=2 | awk "$awk_prog")"
|
||||
readonly unstaged
|
||||
|
||||
if [ "$unstaged" != '' ]; then
|
||||
printf 'WARNING: you have unstaged changes:\n\n%s\n\n' "$unstaged"
|
||||
prompt
|
||||
fi
|
||||
|
||||
# Warn the programmer about temporary todos and skel FIXMEs, but do not fail the
|
||||
# commit, because the commit could be in a temporary branch.
|
||||
temp_todos="$(
|
||||
git grep -e 'TODO.*!!' -- \
|
||||
':!./scripts/hooks/pre-commit' \
|
||||
|| :
|
||||
)"
|
||||
readonly temp_todos
|
||||
|
||||
if [ "$temp_todos" != '' ]; then
|
||||
printf 'WARNING: you have temporary todos:\n\n%s\n\n' "$temp_todos"
|
||||
prompt
|
||||
fi
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$(git diff --cached --name-only -- '*.md' || :)" != '' ]; then
|
||||
make VERBOSE="$verbose" lint-md
|
||||
fi
|
||||
|
||||
if [ "$(git diff --cached --name-only -- '*.rs' 'Cargo.toml' 'Cargo.lock' || :)" != '' ]; then
|
||||
make VERBOSE="$verbose" lint-rust test-rust
|
||||
fi
|
||||
|
||||
if [ "$(git diff --cached --name-only -- '*.cpp' '*.h' || :)" != '' ]; then
|
||||
make VERBOSE="$verbose" lint-cpp test-cpp
|
||||
fi
|
||||
@@ -63,10 +63,10 @@ typedef enum {
|
||||
} TcpipEvent;
|
||||
|
||||
typedef struct {
|
||||
uint64_t id; /**< generated identifier of request for connection */
|
||||
int proto; /**< connection protocol */
|
||||
const SocketAddress *src; /**< source address of connection */
|
||||
const SocketAddress *dst; /**< destination address of connection */
|
||||
uint64_t id; /**< generated identifier of request for connection */
|
||||
int proto; /**< connection protocol */
|
||||
const SocketAddress *src; /**< source address of connection */
|
||||
const SocketAddress *dst; /**< destination address of connection */
|
||||
} TcpipConnectRequestEvent;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -17,7 +17,6 @@ void libevent_lwip_log_debug(const char *message, ...);
|
||||
libevent_lwip_log_debug x; \
|
||||
} while (0)
|
||||
|
||||
|
||||
// Determine endianess
|
||||
#if defined(_WIN32) && !defined(__clang__)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "vpn/utils.h"
|
||||
#include "tcpip/tcpip.h"
|
||||
#include "vpn/utils.h"
|
||||
|
||||
namespace ag {
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# <p align="center">TrustTunnel CLI Client</p>
|
||||
|
||||
<p align="center">Simple, fast and secure CLI client for the TrustTunnel VPN</p>
|
||||
# TrustTunnel CLI Client
|
||||
|
||||
TrustTunnel CLI Client is an application built on top of the TrustTunnel Client Libraries.
|
||||
It provides an easy-to-use interface to configure and connect to an TrustTunnel endpoint.
|
||||
@@ -45,7 +43,7 @@ For a more customized configuration experience, follow the steps below:
|
||||
|
||||
#### Setup Wizard CLI Options
|
||||
|
||||
```
|
||||
```text
|
||||
Usage: setup_wizard [OPTIONS]
|
||||
|
||||
Options:
|
||||
@@ -65,11 +63,13 @@ Options:
|
||||
#### Examples
|
||||
|
||||
**Interactive mode** (guided setup):
|
||||
|
||||
```shell
|
||||
./setup_wizard
|
||||
```
|
||||
|
||||
**Non-interactive with endpoint config** (recommended):
|
||||
|
||||
```shell
|
||||
./setup_wizard --mode non-interactive \
|
||||
--endpoint_config <endpoint_config.toml> \
|
||||
@@ -77,6 +77,7 @@ Options:
|
||||
```
|
||||
|
||||
**Non-interactive with manual parameters**:
|
||||
|
||||
```shell
|
||||
./setup_wizard --mode non-interactive \
|
||||
--address 192.168.1.100:443 \
|
||||
@@ -95,7 +96,7 @@ The configuration file uses TOML format. Below are all available settings.
|
||||
### Top-Level Settings
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| -------- | ---- | ------- | ----------- |
|
||||
| `loglevel` | string | `"info"` | Logging level: `info`, `debug`, `trace` |
|
||||
| `vpn_mode` | string | `"general"` | Routing policy: `general` (route all except exclusions) or `selective` (route only exclusions) |
|
||||
| `killswitch_enabled` | bool | `true` | Block traffic when VPN connection is lost |
|
||||
@@ -107,7 +108,7 @@ The configuration file uses TOML format. Below are all available settings.
|
||||
### Endpoint Settings (`[endpoint]`)
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| -------- | ---- | ------- | ----------- |
|
||||
| `hostname` | string | *required* | Endpoint hostname for TLS session establishment |
|
||||
| `addresses` | array[string] | *required* | Endpoint IP:port addresses (pinger selects best) |
|
||||
| `has_ipv6` | bool | `true` | Whether IPv6 traffic can be routed through endpoint |
|
||||
@@ -123,7 +124,7 @@ The configuration file uses TOML format. Below are all available settings.
|
||||
### TUN Listener Settings (`[listener.tun]`)
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| -------- | ---- | ------- | ----------- |
|
||||
| `bound_if` | string | auto-detected | Network interface for VPN client connections (Linux/Windows: auto, macOS: `en0`) |
|
||||
| `included_routes` | array[string] | `["0.0.0.0/0", "2000::/3"]` | Routes in CIDR notation to set to the virtual interface |
|
||||
| `excluded_routes` | array[string] | `["0.0.0.0/8", "10.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.168.0.0/16", "224.0.0.0/3"]` | Routes in CIDR notation to exclude from VPN routing |
|
||||
@@ -132,7 +133,7 @@ The configuration file uses TOML format. Below are all available settings.
|
||||
### SOCKS Listener Settings (`[listener.socks]`)
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| -------- | ---- | ------- | ----------- |
|
||||
| `address` | string | `"127.0.0.1:1080"` | IP address and port to bind the SOCKS5 proxy |
|
||||
| `username` | string | `null` | Username for SOCKS authentication (optional) |
|
||||
| `password` | string | `null` | Password for SOCKS authentication (optional) |
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/logger.h"
|
||||
#include "common/autofd.h"
|
||||
#include "common/logger.h"
|
||||
#include "config.h"
|
||||
#include "net/os_tunnel.h"
|
||||
#include "net/utils.h"
|
||||
@@ -60,7 +60,9 @@ public:
|
||||
TrustTunnelClient &operator=(TrustTunnelClient &&c) = delete;
|
||||
|
||||
struct AutoSetup {};
|
||||
struct UseTunnelFd { AutoFd fd; };
|
||||
struct UseTunnelFd {
|
||||
AutoFd fd;
|
||||
};
|
||||
struct UseProcessPackets {};
|
||||
|
||||
using ListenerSettings = std::variant<AutoSetup, UseTunnelFd, UseProcessPackets>;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::fs;
|
||||
use toml_edit::{Array, Document, Item, Table, value};
|
||||
use crate::settings::{Listener, Settings};
|
||||
use crate::template_settings;
|
||||
use crate::template_settings::ToTomlComment;
|
||||
use std::fs;
|
||||
use toml_edit::{value, Array, Document, Item, Table};
|
||||
|
||||
pub fn compose_document(file: Option<&str>, settings: &Settings) -> Document {
|
||||
let doc = match file {
|
||||
@@ -29,8 +29,8 @@ fn fabricate_template_document() -> Document {
|
||||
template_settings::ENDPOINT.as_str(),
|
||||
template_settings::COMMON_LISTENER_TABLE,
|
||||
)
|
||||
.parse()
|
||||
.expect("Couldn't parse fabricated document")
|
||||
.parse()
|
||||
.expect("Couldn't parse fabricated document")
|
||||
}
|
||||
|
||||
fn fill_main_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
@@ -45,7 +45,8 @@ fn fill_main_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
}
|
||||
|
||||
fn fill_endpoint_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
let endpoint = doc.get_mut("endpoint")
|
||||
let endpoint = doc
|
||||
.get_mut("endpoint")
|
||||
.and_then(Item::as_table_mut)
|
||||
.expect("Endpoint table not found");
|
||||
|
||||
@@ -57,19 +58,22 @@ fn fill_endpoint_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
endpoint["client_random"] = value(&settings.endpoint.client_random);
|
||||
endpoint["skip_verification"] = value(settings.endpoint.skip_verification);
|
||||
endpoint["anti_dpi"] = value(settings.endpoint.anti_dpi);
|
||||
endpoint["certificate"] = value(
|
||||
settings.endpoint.certificate.as_deref().unwrap_or_default()
|
||||
);
|
||||
endpoint["certificate"] = value(settings.endpoint.certificate.as_deref().unwrap_or_default());
|
||||
endpoint["upstream_protocol"] = value(&settings.endpoint.upstream_protocol);
|
||||
endpoint["upstream_fallback_protocol"] = value(
|
||||
settings.endpoint.upstream_fallback_protocol.as_deref().unwrap_or_default()
|
||||
settings
|
||||
.endpoint
|
||||
.upstream_fallback_protocol
|
||||
.as_deref()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
fn fill_listener_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
let mut listener = doc.get_mut("listener")
|
||||
let mut listener = doc
|
||||
.get_mut("listener")
|
||||
.and_then(Item::as_table_mut)
|
||||
.expect("Listener table not found");
|
||||
|
||||
@@ -92,9 +96,10 @@ fn fill_listener_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
_ => unreachable!(),
|
||||
},
|
||||
)
|
||||
.parse()
|
||||
.expect("Couldn't parse rebuilt document");
|
||||
listener = doc.get_mut("listener")
|
||||
.parse()
|
||||
.expect("Couldn't parse rebuilt document");
|
||||
listener = doc
|
||||
.get_mut("listener")
|
||||
.and_then(Item::as_table_mut)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -109,7 +114,8 @@ fn fill_listener_table(mut doc: Document, settings: &Settings) -> Document {
|
||||
}
|
||||
|
||||
fn fill_socks_listener_table(table: &mut Table, settings: &Settings) {
|
||||
let table = table["socks"].as_table_mut()
|
||||
let table = table["socks"]
|
||||
.as_table_mut()
|
||||
.expect("SOCKS listener table not found");
|
||||
let settings = match &settings.listener {
|
||||
Listener::Socks(x) => x,
|
||||
@@ -122,7 +128,8 @@ fn fill_socks_listener_table(table: &mut Table, settings: &Settings) {
|
||||
}
|
||||
|
||||
fn fill_tun_listener_table(table: &mut Table, settings: &Settings) {
|
||||
let table = table["tun"].as_table_mut()
|
||||
let table = table["tun"]
|
||||
.as_table_mut()
|
||||
.expect("TUN listener table not found");
|
||||
let settings = match &settings.listener {
|
||||
Listener::Tun(x) => x,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::settings::{Endpoint, Settings};
|
||||
use crate::user_interaction::{ask_for_input, checked_overwrite, select_index};
|
||||
use std::fs;
|
||||
use std::ops::Not;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use crate::settings::{Endpoint, Settings};
|
||||
use crate::user_interaction::{ask_for_input, checked_overwrite, select_index};
|
||||
|
||||
mod composer;
|
||||
mod settings;
|
||||
@@ -44,11 +44,13 @@ pub struct PredefinedParameters {
|
||||
impl PredefinedParameters {
|
||||
pub fn new(args: &clap::ArgMatches) -> PredefinedParameters {
|
||||
PredefinedParameters {
|
||||
endpoint_addresses: args.get_many::<String>(ENDPOINT_ADDRESS_PARAM_NAME)
|
||||
endpoint_addresses: args
|
||||
.get_many::<String>(ENDPOINT_ADDRESS_PARAM_NAME)
|
||||
.map(Iterator::cloned)
|
||||
.map(Iterator::collect),
|
||||
hostname: args.get_one::<String>(HOSTNAME_PARAM_NAME).cloned(),
|
||||
credentials: args.get_one::<String>(CREDENTIALS_PARAM_NAME)
|
||||
credentials: args
|
||||
.get_one::<String>(CREDENTIALS_PARAM_NAME)
|
||||
.map(|x| x.splitn(2, ':'))
|
||||
.and_then(|mut x| x.next().zip(x.next()))
|
||||
.map(|(a, b)| (a.to_string(), b.to_string())),
|
||||
@@ -154,8 +156,8 @@ Required in non-interactive mode."#),
|
||||
);
|
||||
let args = command.clone().get_matches();
|
||||
|
||||
|
||||
*MODE.lock().unwrap() = match args.get_one::<String>(MODE_PARAM_NAME)
|
||||
*MODE.lock().unwrap() = match args
|
||||
.get_one::<String>(MODE_PARAM_NAME)
|
||||
.map(String::as_str)
|
||||
.unwrap_or(MODE_INTERACTIVE)
|
||||
{
|
||||
@@ -165,10 +167,12 @@ Required in non-interactive mode."#),
|
||||
};
|
||||
|
||||
if get_mode() == Mode::NonInteractive {
|
||||
if !(args.contains_id(ENDPOINT_CONFIG_PARAM_NAME)
|
||||
|| args.contains_id(HOSTNAME_PARAM_NAME)) {
|
||||
command.error(clap::error::ErrorKind::MissingRequiredArgument,
|
||||
r#"Additional arguments required for non-interactive mode
|
||||
if !(args.contains_id(ENDPOINT_CONFIG_PARAM_NAME) || args.contains_id(HOSTNAME_PARAM_NAME))
|
||||
{
|
||||
command
|
||||
.error(
|
||||
clap::error::ErrorKind::MissingRequiredArgument,
|
||||
r#"Additional arguments required for non-interactive mode
|
||||
|
||||
Must be provided either:
|
||||
1. All required options separatelly:
|
||||
@@ -178,14 +182,15 @@ OR
|
||||
2. A configuration file generated on endpoint:
|
||||
--endpoint_config <endpoint_config>
|
||||
|
||||
Note: Cannot mix both variants"#).exit();
|
||||
Note: Cannot mix both variants"#,
|
||||
)
|
||||
.exit();
|
||||
}
|
||||
}
|
||||
|
||||
*PREDEFINED_PARAMS.lock().unwrap() = PredefinedParameters::new(&args);
|
||||
|
||||
(get_mode() == Mode::Interactive)
|
||||
.then(|| { println!("Welcome to the setup wizard")});
|
||||
(get_mode() == Mode::Interactive).then(|| println!("Welcome to the setup wizard"));
|
||||
|
||||
let settings_path = {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -195,48 +200,48 @@ Note: Cannot mix both variants"#).exit();
|
||||
MakeFromScratch,
|
||||
}
|
||||
|
||||
let action =
|
||||
if let Some((path, settings)) = get_mode().eq(&Mode::NonInteractive).not()
|
||||
.then(|| find_existent_settings::<Settings>("."))
|
||||
.flatten()
|
||||
{
|
||||
let selection = select_index(
|
||||
format!("Found existing settings: {path}."),
|
||||
&["Use it", "Modify and overwrite", "Make new from scratch"],
|
||||
Some(0),
|
||||
);
|
||||
match selection {
|
||||
0 => Action::UseExisting { path },
|
||||
1 => Action::ModifyAndOverwrite { path, settings },
|
||||
2 => Action::MakeFromScratch,
|
||||
_ => unreachable!("{:?}", selection),
|
||||
}
|
||||
} else {
|
||||
Action::MakeFromScratch
|
||||
};
|
||||
let action = if let Some((path, settings)) = get_mode()
|
||||
.eq(&Mode::NonInteractive)
|
||||
.not()
|
||||
.then(|| find_existent_settings::<Settings>("."))
|
||||
.flatten()
|
||||
{
|
||||
let selection = select_index(
|
||||
format!("Found existing settings: {path}."),
|
||||
&["Use it", "Modify and overwrite", "Make new from scratch"],
|
||||
Some(0),
|
||||
);
|
||||
match selection {
|
||||
0 => Action::UseExisting { path },
|
||||
1 => Action::ModifyAndOverwrite { path, settings },
|
||||
2 => Action::MakeFromScratch,
|
||||
_ => unreachable!("{:?}", selection),
|
||||
}
|
||||
} else {
|
||||
Action::MakeFromScratch
|
||||
};
|
||||
match action {
|
||||
Action::UseExisting { path } => path,
|
||||
Action::ModifyAndOverwrite { path, settings } => {
|
||||
(get_mode() == Mode::Interactive)
|
||||
.then(|| { println!("Let's build the settings") });
|
||||
(get_mode() == Mode::Interactive).then(|| println!("Let's build the settings"));
|
||||
let settings = settings::build(Some(&settings));
|
||||
println!("The settings are successfully built\n");
|
||||
|
||||
let doc = composer::compose_document(Some(&path), &settings);
|
||||
fs::write(&path, doc.to_string())
|
||||
.expect("Couldn't write the settings to a file");
|
||||
fs::write(&path, doc.to_string()).expect("Couldn't write the settings to a file");
|
||||
|
||||
path
|
||||
}
|
||||
Action::MakeFromScratch => {
|
||||
(get_mode() == Mode::Interactive)
|
||||
.then(|| { println!("Let's build the settings") });
|
||||
(get_mode() == Mode::Interactive).then(|| println!("Let's build the settings"));
|
||||
let settings = settings::build(None);
|
||||
println!("The settings are successfully built\n");
|
||||
|
||||
let path = ask_for_input::<String>(
|
||||
"Path to a file to store the settings",
|
||||
get_predefined_params().settings_file.clone()
|
||||
get_predefined_params()
|
||||
.settings_file
|
||||
.clone()
|
||||
.or(Some("trusttunnel_client.toml".into())),
|
||||
);
|
||||
if checked_overwrite(&path, "Overwrite the existing settings file?") {
|
||||
@@ -256,10 +261,15 @@ Note: Cannot mix both variants"#).exit();
|
||||
}
|
||||
|
||||
fn find_existent_settings<T: serde::de::DeserializeOwned>(path: &str) -> Option<(String, T)> {
|
||||
fs::read_dir(path).ok()?
|
||||
fs::read_dir(path)
|
||||
.ok()?
|
||||
.filter_map(Result::ok)
|
||||
.filter(|entry| entry.metadata()
|
||||
.map(|meta| meta.is_file()).unwrap_or_default())
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.metadata()
|
||||
.map(|meta| meta.is_file())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.filter_map(|entry| entry.file_name().into_string().ok())
|
||||
.filter_map(|fname| fs::read_to_string(&fname).ok().zip(Some(fname)))
|
||||
.find_map(|(content, fname)| Some(fname).zip(toml::from_str::<T>(&content).ok()))
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use std::{fs, path};
|
||||
use crate::user_interaction::{
|
||||
ask_for_agreement, ask_for_agreement_with_default, ask_for_input, ask_for_password,
|
||||
checked_overwrite, select_variant,
|
||||
};
|
||||
use crate::Mode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::ops::Not;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path};
|
||||
use x509_parser::extensions::GeneralName;
|
||||
use crate::Mode;
|
||||
use crate::user_interaction::{ask_for_agreement, ask_for_agreement_with_default, ask_for_input, ask_for_password, checked_overwrite, select_variant};
|
||||
|
||||
macro_rules! docgen {
|
||||
(
|
||||
@@ -260,7 +263,8 @@ impl Listener {
|
||||
match self {
|
||||
Listener::Socks(_) => "socks",
|
||||
Listener::Tun(_) => "tun",
|
||||
}.into()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,18 +276,11 @@ impl SocksListener {
|
||||
|
||||
impl TunListener {
|
||||
pub fn default_bound_if() -> String {
|
||||
if cfg!(target_os = "macos") {
|
||||
"en0"
|
||||
} else {
|
||||
""
|
||||
}.into()
|
||||
if cfg!(target_os = "macos") { "en0" } else { "" }.into()
|
||||
}
|
||||
|
||||
pub fn default_included_routes() -> Vec<String> {
|
||||
vec![
|
||||
"0.0.0.0/0".into(),
|
||||
"2000::/3".into(),
|
||||
]
|
||||
vec!["0.0.0.0/0".into(), "2000::/3".into()]
|
||||
}
|
||||
|
||||
pub fn default_excluded_routes() -> Vec<String> {
|
||||
@@ -304,30 +301,39 @@ impl TunListener {
|
||||
macro_rules! opt_field {
|
||||
($x:expr, $field:ident) => {
|
||||
$x.map(|x| &x.$field)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn build(template: Option<&Settings>) -> Settings {
|
||||
Settings {
|
||||
loglevel: opt_field!(template, loglevel).cloned()
|
||||
loglevel: opt_field!(template, loglevel)
|
||||
.cloned()
|
||||
.unwrap_or_else(Settings::default_loglevel),
|
||||
vpn_mode: select_variant(
|
||||
format!("{}\n", Settings::doc_vpn_mode()),
|
||||
Settings::available_vpn_modes(),
|
||||
Settings::available_vpn_modes().iter()
|
||||
.position(|x| *x == opt_field!(template, vpn_mode).cloned()
|
||||
Settings::available_vpn_modes().iter().position(|x| {
|
||||
*x == opt_field!(template, vpn_mode)
|
||||
.cloned()
|
||||
.unwrap_or_else(Settings::default_vpn_mode)
|
||||
.as_str()),
|
||||
).into(),
|
||||
killswitch_enabled: opt_field!(template, killswitch_enabled).cloned()
|
||||
.as_str()
|
||||
}),
|
||||
)
|
||||
.into(),
|
||||
killswitch_enabled: opt_field!(template, killswitch_enabled)
|
||||
.cloned()
|
||||
.unwrap_or_else(Settings::default_killswitch_enabled),
|
||||
killswitch_allow_ports: opt_field!(template, killswitch_allow_ports).cloned()
|
||||
killswitch_allow_ports: opt_field!(template, killswitch_allow_ports)
|
||||
.cloned()
|
||||
.unwrap_or_else(Settings::default_killswitch_allow_ports),
|
||||
post_quantum_group_enabled: opt_field!(template, post_quantum_group_enabled).cloned()
|
||||
post_quantum_group_enabled: opt_field!(template, post_quantum_group_enabled)
|
||||
.cloned()
|
||||
.unwrap_or_else(Settings::default_post_quantum_group_enabled),
|
||||
exclusions: opt_field!(template, exclusions).cloned()
|
||||
exclusions: opt_field!(template, exclusions)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
dns_upstreams: opt_field!(template, dns_upstreams).cloned()
|
||||
dns_upstreams: opt_field!(template, dns_upstreams)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
endpoint: build_endpoint(opt_field!(template, endpoint)),
|
||||
listener: build_listener(opt_field!(template, listener)),
|
||||
@@ -336,94 +342,106 @@ pub fn build(template: Option<&Settings>) -> Settings {
|
||||
|
||||
fn build_endpoint(template: Option<&Endpoint>) -> Endpoint {
|
||||
let predefined_params = crate::get_predefined_params().clone();
|
||||
let endpoint_config: Option<EndpointConfig> = empty_to_none(ask_for_input("Path to endpoint config, empty if no", predefined_params.endpoint_config.or(Some("".to_string()))))
|
||||
.and_then(|x| {
|
||||
fs::read_to_string(&x)
|
||||
.map_err(|e| { panic!("Failed to read endpoint config file:\n{}", e.to_string()) })
|
||||
.ok()
|
||||
})
|
||||
.and_then(|x| {
|
||||
toml::de::from_str(x.as_str())
|
||||
.map_err(|e| { panic!("Failed to parse endpoint config:\n{}", e.to_string()) })
|
||||
.ok()
|
||||
});
|
||||
let endpoint_config: Option<EndpointConfig> = empty_to_none(ask_for_input(
|
||||
"Path to endpoint config, empty if no",
|
||||
predefined_params.endpoint_config.or(Some("".to_string())),
|
||||
))
|
||||
.and_then(|x| {
|
||||
fs::read_to_string(&x)
|
||||
.map_err(|e| panic!("Failed to read endpoint config file:\n{}", e.to_string()))
|
||||
.ok()
|
||||
})
|
||||
.and_then(|x| {
|
||||
toml::de::from_str(x.as_str())
|
||||
.map_err(|e| panic!("Failed to parse endpoint config:\n{}", e.to_string()))
|
||||
.ok()
|
||||
});
|
||||
let mut x = Endpoint {
|
||||
addresses: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.addresses.clone().into()
|
||||
})
|
||||
addresses: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.addresses.clone().into())
|
||||
.or_else(|| {
|
||||
ask_for_input::<String>(
|
||||
&format!("{}\nMust be delimited by whitespace.\n", Endpoint::doc_addresses()),
|
||||
predefined_params.endpoint_addresses
|
||||
&format!(
|
||||
"{}\nMust be delimited by whitespace.\n",
|
||||
Endpoint::doc_addresses()
|
||||
),
|
||||
predefined_params
|
||||
.endpoint_addresses
|
||||
.or(opt_field!(template, addresses).cloned())
|
||||
.map(|x| x.join(" ")),
|
||||
)
|
||||
.split_whitespace()
|
||||
.map(String::from)
|
||||
.collect::<Vec<String>>().into()
|
||||
)
|
||||
.split_whitespace()
|
||||
.map(String::from)
|
||||
.collect::<Vec<String>>()
|
||||
.into()
|
||||
})
|
||||
.unwrap(),
|
||||
has_ipv6: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.has_ipv6.into()
|
||||
})
|
||||
has_ipv6: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.has_ipv6.into())
|
||||
.or(opt_field!(template, has_ipv6).cloned())
|
||||
.unwrap_or_else(Endpoint::default_has_ipv6),
|
||||
username: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.username.clone().into()
|
||||
})
|
||||
username: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.username.clone().into())
|
||||
.or_else(|| {
|
||||
ask_for_input(
|
||||
Endpoint::doc_username(),
|
||||
predefined_params.credentials.clone().unzip().0
|
||||
.or(opt_field!(template, username).cloned())
|
||||
)
|
||||
.into()
|
||||
predefined_params
|
||||
.credentials
|
||||
.clone()
|
||||
.unzip()
|
||||
.0
|
||||
.or(opt_field!(template, username).cloned()),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
.unwrap(),
|
||||
password: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.password.clone().into()
|
||||
})
|
||||
password: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.password.clone().into())
|
||||
.or_else(|| {
|
||||
predefined_params.credentials.unzip().1
|
||||
.unwrap_or_else(|| opt_field!(template, password).cloned()
|
||||
.and_then(empty_to_none)
|
||||
.and_then(|x| ask_for_agreement("Overwrite password?").not().then_some(x))
|
||||
.unwrap_or_else(|| ask_for_password(Endpoint::doc_password()))
|
||||
)
|
||||
predefined_params
|
||||
.credentials
|
||||
.unzip()
|
||||
.1
|
||||
.unwrap_or_else(|| {
|
||||
opt_field!(template, password)
|
||||
.cloned()
|
||||
.and_then(empty_to_none)
|
||||
.and_then(|x| {
|
||||
ask_for_agreement("Overwrite password?").not().then_some(x)
|
||||
})
|
||||
.unwrap_or_else(|| ask_for_password(Endpoint::doc_password()))
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.unwrap(),
|
||||
client_random: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.client_random.clone().into()
|
||||
})
|
||||
client_random: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.client_random.clone().into())
|
||||
.or(opt_field!(template, client_random).cloned())
|
||||
.unwrap_or_default(),
|
||||
skip_verification: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.skip_verification.into()
|
||||
})
|
||||
skip_verification: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.skip_verification.into())
|
||||
.or(opt_field!(template, skip_verification).cloned())
|
||||
.unwrap_or_else(Endpoint::default_skip_verification),
|
||||
upstream_protocol: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.upstream_protocol.clone().into()
|
||||
})
|
||||
upstream_protocol: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.upstream_protocol.clone().into())
|
||||
.or(opt_field!(template, upstream_protocol).cloned())
|
||||
.unwrap_or_else(Endpoint::default_upstream_protocol),
|
||||
upstream_fallback_protocol: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.upstream_fallback_protocol.clone().into()
|
||||
})
|
||||
.or(opt_field!(template, upstream_fallback_protocol).cloned().flatten()),
|
||||
anti_dpi: endpoint_config.as_ref()
|
||||
.and_then(|x| {
|
||||
x.anti_dpi.into()
|
||||
})
|
||||
upstream_fallback_protocol: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.upstream_fallback_protocol.clone().into())
|
||||
.or(opt_field!(template, upstream_fallback_protocol)
|
||||
.cloned()
|
||||
.flatten()),
|
||||
anti_dpi: endpoint_config
|
||||
.as_ref()
|
||||
.and_then(|x| x.anti_dpi.into())
|
||||
.or(opt_field!(template, anti_dpi).cloned())
|
||||
.unwrap_or_else(Endpoint::default_anti_dpi),
|
||||
..Default::default()
|
||||
@@ -435,27 +453,36 @@ fn build_endpoint(template: Option<&Endpoint>) -> Endpoint {
|
||||
x.certificate = config.certificate.clone().into();
|
||||
} else {
|
||||
let (hostname, certificate) = if crate::get_mode() == Mode::NonInteractive {
|
||||
(predefined_params.hostname.clone(), predefined_params.certificate
|
||||
.and_then(|x| {
|
||||
fs::read_to_string(&x).expect("Failed to read certificate").into()
|
||||
}))
|
||||
} else if let Some(cert) = opt_field!(template, certificate).cloned().flatten()
|
||||
.and_then(parse_cert)
|
||||
.and_then(|x|
|
||||
ask_for_agreement(&format!("Use an existent certificate? {:?}", x))
|
||||
.then_some(x)
|
||||
(
|
||||
predefined_params.hostname.clone(),
|
||||
predefined_params.certificate.and_then(|x| {
|
||||
fs::read_to_string(&x)
|
||||
.expect("Failed to read certificate")
|
||||
.into()
|
||||
}),
|
||||
)
|
||||
} else if let Some(cert) = opt_field!(template, certificate)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.and_then(parse_cert)
|
||||
.and_then(|x| {
|
||||
ask_for_agreement(&format!("Use an existent certificate? {:?}", x)).then_some(x)
|
||||
})
|
||||
{
|
||||
(Some(cert.common_name), opt_field!(template, certificate).cloned().flatten())
|
||||
(
|
||||
Some(cert.common_name),
|
||||
opt_field!(template, certificate).cloned().flatten(),
|
||||
)
|
||||
} else if let Some(cert) = empty_to_none(ask_for_input::<String>(
|
||||
&format!("{}\nEnter a path to certificate:", Endpoint::doc_certificate()),
|
||||
&format!(
|
||||
"{}\nEnter a path to certificate:",
|
||||
Endpoint::doc_certificate()
|
||||
),
|
||||
Some("".into()),
|
||||
)) {
|
||||
let contents = fs::read_to_string(& cert).expect("Failed to read certificate");
|
||||
let contents = fs::read_to_string(&cert).expect("Failed to read certificate");
|
||||
match parse_cert(contents.clone()) {
|
||||
Some(parsed) => {
|
||||
(Some(parsed.common_name), Some(contents))
|
||||
}
|
||||
Some(parsed) => (Some(parsed.common_name), Some(contents)),
|
||||
None => {
|
||||
panic!("Couldn't parse provided certificate");
|
||||
}
|
||||
@@ -466,7 +493,8 @@ fn build_endpoint(template: Option<&Endpoint>) -> Endpoint {
|
||||
|
||||
x.hostname = ask_for_input(
|
||||
Endpoint::doc_hostname(),
|
||||
predefined_params.hostname
|
||||
predefined_params
|
||||
.hostname
|
||||
.or(opt_field!(template, hostname).cloned())
|
||||
.or(hostname),
|
||||
);
|
||||
@@ -479,9 +507,11 @@ fn build_endpoint(template: Option<&Endpoint>) -> Endpoint {
|
||||
|
||||
x.skip_verification = x.certificate.is_none()
|
||||
&& ask_for_agreement_with_default(
|
||||
&format!("{}\n", Endpoint::doc_skip_verification()),
|
||||
opt_field!(template, skip_verification).cloned().unwrap_or_default(),
|
||||
);
|
||||
&format!("{}\n", Endpoint::doc_skip_verification()),
|
||||
opt_field!(template, skip_verification)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
x
|
||||
}
|
||||
@@ -493,10 +523,12 @@ fn build_listener(template: Option<&Listener>) -> Listener {
|
||||
* tun: TUN device.
|
||||
"#,
|
||||
Listener::available_kinds(),
|
||||
Listener::available_kinds().iter()
|
||||
.position(|x| *x == template.map(Listener::to_kind_string)
|
||||
Listener::available_kinds().iter().position(|x| {
|
||||
*x == template
|
||||
.map(Listener::to_kind_string)
|
||||
.unwrap_or_else(Listener::default_kind)
|
||||
.as_str()),
|
||||
.as_str()
|
||||
}),
|
||||
) {
|
||||
"socks" => {
|
||||
let template = template.and_then(|x| match x {
|
||||
@@ -506,16 +538,29 @@ fn build_listener(template: Option<&Listener>) -> Listener {
|
||||
Listener::Socks(SocksListener {
|
||||
address: ask_for_input(
|
||||
SocksListener::doc_address(),
|
||||
Some(opt_field!(template, address).cloned()
|
||||
.unwrap_or_else(SocksListener::default_address)),
|
||||
Some(
|
||||
opt_field!(template, address)
|
||||
.cloned()
|
||||
.unwrap_or_else(SocksListener::default_address),
|
||||
),
|
||||
),
|
||||
username: empty_to_none(ask_for_input(
|
||||
SocksListener::doc_username(),
|
||||
Some(opt_field!(template, username).cloned().flatten().unwrap_or_default()),
|
||||
Some(
|
||||
opt_field!(template, username)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
)),
|
||||
password: empty_to_none(ask_for_input(
|
||||
SocksListener::doc_password(),
|
||||
Some(opt_field!(template, password).cloned().flatten().unwrap_or_default()),
|
||||
Some(
|
||||
opt_field!(template, password)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
)),
|
||||
})
|
||||
}
|
||||
@@ -530,15 +575,21 @@ fn build_listener(template: Option<&Listener>) -> Listener {
|
||||
} else {
|
||||
ask_for_input(
|
||||
TunListener::doc_bound_if(),
|
||||
Some(opt_field!(template, bound_if).cloned()
|
||||
.unwrap_or_else(TunListener::default_bound_if)),
|
||||
Some(
|
||||
opt_field!(template, bound_if)
|
||||
.cloned()
|
||||
.unwrap_or_else(TunListener::default_bound_if),
|
||||
),
|
||||
)
|
||||
},
|
||||
included_routes: opt_field!(template, included_routes).cloned()
|
||||
included_routes: opt_field!(template, included_routes)
|
||||
.cloned()
|
||||
.unwrap_or_else(TunListener::default_included_routes),
|
||||
excluded_routes: opt_field!(template, excluded_routes).cloned()
|
||||
excluded_routes: opt_field!(template, excluded_routes)
|
||||
.cloned()
|
||||
.unwrap_or_else(TunListener::default_excluded_routes),
|
||||
mtu_size: opt_field!(template, mtu_size).cloned()
|
||||
mtu_size: opt_field!(template, mtu_size)
|
||||
.cloned()
|
||||
.unwrap_or_else(TunListener::default_mtu_size),
|
||||
})
|
||||
}
|
||||
@@ -586,9 +637,15 @@ struct Cert {
|
||||
}
|
||||
|
||||
fn lookup_existent_cert() -> Option<Cert> {
|
||||
fs::read_dir(".").ok()?
|
||||
fs::read_dir(".")
|
||||
.ok()?
|
||||
.filter_map(Result::ok)
|
||||
.filter(|entry| entry.metadata().map(|meta| meta.is_file()).unwrap_or_default())
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.metadata()
|
||||
.map(|meta| meta.is_file())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.filter_map(|entry| entry.path().to_str().map(String::from))
|
||||
.find_map(parse_cert)
|
||||
}
|
||||
@@ -601,16 +658,24 @@ fn parse_cert(contents: String) -> Option<Cert> {
|
||||
.next()?;
|
||||
let cert = x509_parser::parse_x509_certificate(&cert.0).ok()?.1;
|
||||
Some(Cert {
|
||||
common_name: cert.validity.is_valid()
|
||||
.then(|| {
|
||||
let x = cert.subject.to_string();
|
||||
x.as_str()
|
||||
.strip_prefix("CN=")
|
||||
.map(String::from)
|
||||
.unwrap_or(x)
|
||||
})?,
|
||||
alt_names: cert.subject_alternative_name().ok().flatten()
|
||||
.map(|x| x.value.general_names.iter().map(GeneralName::to_string).collect())
|
||||
common_name: cert.validity.is_valid().then(|| {
|
||||
let x = cert.subject.to_string();
|
||||
x.as_str()
|
||||
.strip_prefix("CN=")
|
||||
.map(String::from)
|
||||
.unwrap_or(x)
|
||||
})?,
|
||||
alt_names: cert
|
||||
.subject_alternative_name()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|x| {
|
||||
x.value
|
||||
.general_names
|
||||
.iter()
|
||||
.map(GeneralName::to_string)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
expiration_date: cert.validity.not_after.to_string(),
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::iter::Iterator;
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::settings::{Endpoint, Settings, SocksListener, TunListener};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::iter::Iterator;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
const OS_LINE_ENDING: &str = "\n";
|
||||
@@ -26,8 +26,9 @@ impl ToTomlComment for String {
|
||||
}
|
||||
}
|
||||
|
||||
pub static MAIN_TABLE: Lazy<String> = Lazy::new(|| format!(
|
||||
r#"{}
|
||||
pub static MAIN_TABLE: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
r#"{}
|
||||
loglevel = "{}"
|
||||
|
||||
{}
|
||||
@@ -48,21 +49,23 @@ exclusions = []
|
||||
{}
|
||||
dns_upstreams = []
|
||||
"#,
|
||||
Settings::doc_loglevel().to_toml_comment(),
|
||||
Settings::default_loglevel(),
|
||||
Settings::doc_vpn_mode().to_toml_comment(),
|
||||
Settings::default_vpn_mode(),
|
||||
Settings::doc_killswitch_enabled().to_toml_comment(),
|
||||
Settings::default_killswitch_enabled(),
|
||||
Settings::doc_killswitch_allow_ports().to_toml_comment(),
|
||||
Settings::doc_post_quantum_group_enabled().to_toml_comment(),
|
||||
Settings::default_post_quantum_group_enabled(),
|
||||
Settings::doc_exclusions().to_toml_comment(),
|
||||
Settings::doc_dns_upstreams().to_toml_comment(),
|
||||
));
|
||||
Settings::doc_loglevel().to_toml_comment(),
|
||||
Settings::default_loglevel(),
|
||||
Settings::doc_vpn_mode().to_toml_comment(),
|
||||
Settings::default_vpn_mode(),
|
||||
Settings::doc_killswitch_enabled().to_toml_comment(),
|
||||
Settings::default_killswitch_enabled(),
|
||||
Settings::doc_killswitch_allow_ports().to_toml_comment(),
|
||||
Settings::doc_post_quantum_group_enabled().to_toml_comment(),
|
||||
Settings::default_post_quantum_group_enabled(),
|
||||
Settings::doc_exclusions().to_toml_comment(),
|
||||
Settings::doc_dns_upstreams().to_toml_comment(),
|
||||
)
|
||||
});
|
||||
|
||||
pub static ENDPOINT: Lazy<String> = Lazy::new(|| format!(
|
||||
r#"{}
|
||||
pub static ENDPOINT: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
r#"{}
|
||||
[endpoint]
|
||||
{}
|
||||
hostname = ""
|
||||
@@ -87,21 +90,22 @@ upstream_fallback_protocol = ""
|
||||
{}
|
||||
anti_dpi = false
|
||||
"#,
|
||||
Endpoint::doc().to_toml_comment(),
|
||||
Endpoint::doc_hostname().to_toml_comment(),
|
||||
Endpoint::doc_addresses().to_toml_comment(),
|
||||
Endpoint::doc_has_ipv6().to_toml_comment(),
|
||||
Endpoint::default_has_ipv6(),
|
||||
Endpoint::doc_username().to_toml_comment(),
|
||||
Endpoint::doc_password().to_toml_comment(),
|
||||
Endpoint::doc_client_random().to_toml_comment(),
|
||||
Endpoint::doc_skip_verification().to_toml_comment(),
|
||||
Endpoint::doc_certificate().to_toml_comment(),
|
||||
Endpoint::doc_upstream_protocol().to_toml_comment(),
|
||||
Endpoint::default_upstream_protocol(),
|
||||
Endpoint::doc_upstream_fallback_protocol().to_toml_comment(),
|
||||
Endpoint::doc_anti_dpi().to_toml_comment(),
|
||||
));
|
||||
Endpoint::doc().to_toml_comment(),
|
||||
Endpoint::doc_hostname().to_toml_comment(),
|
||||
Endpoint::doc_addresses().to_toml_comment(),
|
||||
Endpoint::doc_has_ipv6().to_toml_comment(),
|
||||
Endpoint::default_has_ipv6(),
|
||||
Endpoint::doc_username().to_toml_comment(),
|
||||
Endpoint::doc_password().to_toml_comment(),
|
||||
Endpoint::doc_client_random().to_toml_comment(),
|
||||
Endpoint::doc_skip_verification().to_toml_comment(),
|
||||
Endpoint::doc_certificate().to_toml_comment(),
|
||||
Endpoint::doc_upstream_protocol().to_toml_comment(),
|
||||
Endpoint::default_upstream_protocol(),
|
||||
Endpoint::doc_upstream_fallback_protocol().to_toml_comment(),
|
||||
Endpoint::doc_anti_dpi().to_toml_comment(),
|
||||
)
|
||||
});
|
||||
|
||||
pub const COMMON_LISTENER_TABLE: &str = r#"
|
||||
# Defines the way to listen to network traffic by the kind of the nested table.
|
||||
@@ -111,8 +115,9 @@ pub const COMMON_LISTENER_TABLE: &str = r#"
|
||||
[listener]
|
||||
"#;
|
||||
|
||||
pub static SOCKS_LISTENER: Lazy<String> = Lazy::new(|| format!(
|
||||
r#"[listener.socks]
|
||||
pub static SOCKS_LISTENER: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
r#"[listener.socks]
|
||||
{}
|
||||
address = "{}"
|
||||
{}
|
||||
@@ -120,14 +125,16 @@ username = ""
|
||||
{}
|
||||
password = ""
|
||||
"#,
|
||||
SocksListener::doc_address().to_toml_comment(),
|
||||
SocksListener::default_address(),
|
||||
SocksListener::doc_username().to_toml_comment(),
|
||||
SocksListener::doc_password().to_toml_comment(),
|
||||
));
|
||||
SocksListener::doc_address().to_toml_comment(),
|
||||
SocksListener::default_address(),
|
||||
SocksListener::doc_username().to_toml_comment(),
|
||||
SocksListener::doc_password().to_toml_comment(),
|
||||
)
|
||||
});
|
||||
|
||||
pub static TUN_LISTENER: Lazy<String> = Lazy::new(|| format!(
|
||||
r#"[listener.tun]
|
||||
pub static TUN_LISTENER: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
r#"[listener.tun]
|
||||
{}
|
||||
bound_if = "{}"
|
||||
{}
|
||||
@@ -137,20 +144,21 @@ excluded_routes = [{}]
|
||||
{}
|
||||
mtu_size = {}
|
||||
"#,
|
||||
TunListener::doc_bound_if().to_toml_comment(),
|
||||
TunListener::default_bound_if(),
|
||||
TunListener::doc_included_routes().to_toml_comment(),
|
||||
TunListener::default_included_routes()
|
||||
.iter()
|
||||
.map(|x| format!("\"{x}\","))
|
||||
.collect::<Vec<_>>()
|
||||
.join(OS_LINE_ENDING),
|
||||
TunListener::doc_excluded_routes().to_toml_comment(),
|
||||
TunListener::default_excluded_routes()
|
||||
.iter()
|
||||
.map(|x| format!("\"{x}\","))
|
||||
.collect::<Vec<_>>()
|
||||
.join(OS_LINE_ENDING),
|
||||
TunListener::doc_mtu_size().to_toml_comment(),
|
||||
TunListener::default_mtu_size(),
|
||||
));
|
||||
TunListener::doc_bound_if().to_toml_comment(),
|
||||
TunListener::default_bound_if(),
|
||||
TunListener::doc_included_routes().to_toml_comment(),
|
||||
TunListener::default_included_routes()
|
||||
.iter()
|
||||
.map(|x| format!("\"{x}\","))
|
||||
.collect::<Vec<_>>()
|
||||
.join(OS_LINE_ENDING),
|
||||
TunListener::doc_excluded_routes().to_toml_comment(),
|
||||
TunListener::default_excluded_routes()
|
||||
.iter()
|
||||
.map(|x| format!("\"{x}\","))
|
||||
.collect::<Vec<_>>()
|
||||
.join(OS_LINE_ENDING),
|
||||
TunListener::doc_mtu_size().to_toml_comment(),
|
||||
TunListener::default_mtu_size(),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
use crate::Mode;
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use dialoguer::{Confirm, Input, Password, Select};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::fs;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use dialoguer::{Confirm, Input, Password, Select};
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::Mode;
|
||||
|
||||
pub static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
|
||||
|
||||
/// Ask user to enter a value.
|
||||
/// If [`default`] is [`Some`], suggest the value in the prompt.
|
||||
pub fn ask_for_input<T>(message: &str, default: Option<T>) -> T
|
||||
where
|
||||
T: Clone + Default + FromStr + ToString,
|
||||
<T as FromStr>::Err: ToString,
|
||||
where
|
||||
T: Clone + Default + FromStr + ToString,
|
||||
<T as FromStr>::Err: ToString,
|
||||
{
|
||||
if crate::get_mode() == Mode::NonInteractive {
|
||||
return default.expect("Expecting a user input in non-interactive mode");
|
||||
@@ -25,18 +25,24 @@ pub fn ask_for_input<T>(message: &str, default: Option<T>) -> T
|
||||
.with_prompt(message)
|
||||
.show_default(default.is_some())
|
||||
.default(default.unwrap_or_default())
|
||||
.interact().unwrap()
|
||||
.interact()
|
||||
.unwrap()
|
||||
} else {
|
||||
Input::<T>::with_theme(THEME.deref())
|
||||
.with_prompt(message)
|
||||
.interact().unwrap()
|
||||
.interact()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask if one wants to do something (yes/no).
|
||||
/// No by default.
|
||||
pub fn ask_for_agreement(message: &str) -> bool {
|
||||
assert_ne!(crate::get_mode(), Mode::NonInteractive, "Expecting a user input in non-interactive mode");
|
||||
assert_ne!(
|
||||
crate::get_mode(),
|
||||
Mode::NonInteractive,
|
||||
"Expecting a user input in non-interactive mode"
|
||||
);
|
||||
ask_for_agreement_with_default(message, false)
|
||||
}
|
||||
|
||||
@@ -57,7 +63,11 @@ pub fn ask_for_agreement_with_default(message: &str, default: bool) -> bool {
|
||||
|
||||
/// Ask user to enter a password in a secure way
|
||||
pub fn ask_for_password(message: &str) -> String {
|
||||
assert_ne!(crate::get_mode(), Mode::NonInteractive, "Expecting a user input in non-interactive mode");
|
||||
assert_ne!(
|
||||
crate::get_mode(),
|
||||
Mode::NonInteractive,
|
||||
"Expecting a user input in non-interactive mode"
|
||||
);
|
||||
Password::with_theme(THEME.deref())
|
||||
.with_prompt(message)
|
||||
.interact()
|
||||
@@ -67,14 +77,19 @@ pub fn ask_for_password(message: &str) -> String {
|
||||
/// Check if a file exists and if it does, ask if one wants to overwrite it
|
||||
pub fn checked_overwrite(path: &str, message: &str) -> bool {
|
||||
crate::get_mode() == Mode::NonInteractive
|
||||
|| !fs::metadata(Path::new(&path)).as_ref()
|
||||
.map(fs::Metadata::is_file)
|
||||
.unwrap_or_default()
|
||||
|| !fs::metadata(Path::new(&path))
|
||||
.as_ref()
|
||||
.map(fs::Metadata::is_file)
|
||||
.unwrap_or_default()
|
||||
|| ask_for_agreement(message)
|
||||
}
|
||||
|
||||
/// Ask user to select a variant. Returns index of the selected variant.
|
||||
pub fn select_index<S: Into<String>>(prompt: S, variants: &[&str], default: Option<usize>) -> usize {
|
||||
pub fn select_index<S: Into<String>>(
|
||||
prompt: S,
|
||||
variants: &[&str],
|
||||
default: Option<usize>,
|
||||
) -> usize {
|
||||
if crate::get_mode() == Mode::NonInteractive {
|
||||
return default.expect("Expecting a user input in non-interactive mode");
|
||||
}
|
||||
@@ -84,13 +99,15 @@ pub fn select_index<S: Into<String>>(prompt: S, variants: &[&str], default: Opti
|
||||
.items(variants)
|
||||
.report(true)
|
||||
.default(default.unwrap_or_default())
|
||||
.interact_opt().expect("Interaction failure")
|
||||
.interact_opt()
|
||||
.expect("Interaction failure")
|
||||
.expect("None selected")
|
||||
}
|
||||
|
||||
/// Ask user to select a variant. Returns the selected variant.
|
||||
pub fn select_variant<'a, S>(prompt: S, variants: &[&'a str], default: Option<usize>) -> &'a str
|
||||
where S: Into<String>
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
variants[select_index(prompt, variants, default)]
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_interface.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_interface.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
class AppleSleepNotifier {
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user