Compare commits

...

18 commits

Author SHA1 Message Date
Koi to Coco
a8ebe0b9be
fix: use vec! to allocate buffer #213 (#214)
Some checks are pending
Push or PR / build_n_test (macos-latest) (push) Waiting to run
Push or PR / build_n_test (ubuntu-latest) (push) Waiting to run
Push or PR / build_n_test (windows-latest) (push) Waiting to run
Push or PR / build_n_test_android (push) Waiting to run
Push or PR / build_n_test_ios (push) Waiting to run
Push or PR / Check semver (push) Waiting to run
Integration Tests / Proxy Tests (push) Waiting to run
2025-06-29 16:18:25 +08:00
ssrlive
31b0972801 make rustc happy 2025-06-29 16:11:14 +08:00
B. Blechschmidt
9a6c96cf8b chore: publish v0.7.11 2025-06-19 19:16:24 +02:00
B. Blechschmidt
b5fbaa2d19 doc: fix Docker URL in README 2025-06-19 16:08:01 +02:00
B. Blechschmidt
3baa41a1fb fix: support multi-arch musl builds in Dockerfile 2025-06-19 16:05:54 +02:00
ssrlive
0cf4427ef6 setup_logging function 2025-06-19 20:15:47 +08:00
B. Blechschmidt
bc00dcc5ae fix: docker publish workflow 2025-06-19 13:47:48 +02:00
B. Blechschmidt
d87562b8d3 style: add pre-commit-config 2025-06-19 13:43:14 +02:00
B. Blechschmidt
fa09daabac fix: variable ref in publish-docker action 2025-06-19 13:42:57 +02:00
B. Blechschmidt
b36473ced9 feat(Docker): multi-stage Dockerfile with OS-less container 2025-06-19 13:32:01 +02:00
B. Blechschmidt
584bdc17ed feat(Linux): phase out reliance on iproute2 2025-06-17 01:10:25 +02:00
ssrlive
1880396822 use ctrlc2 async feature 2025-06-14 13:05:54 +08:00
Paper-Dragon
8b4ecabd8f
build image based on alpine/musl (#212) 2025-06-11 14:12:02 +08:00
B. Blechschmidt
fbc47a3001 fix(ci): account for change in load_dotenv 2025-06-11 00:21:03 +02:00
ssrlive
88d31ce168 Bump version 0.7.10 2025-06-03 14:05:28 +08:00
dependabot[bot]
ddebf5ee50
Update tun requirement from 0.7 to 0.8 (#209) 2025-06-03 12:02:31 +08:00
ssrlive
8cdb4f535d
Significant change in --setup parameter (#207) 2025-06-02 10:31:44 +08:00
ssrlive
6a5692cea0 refine code 2025-05-21 15:45:48 +08:00
22 changed files with 221 additions and 135 deletions

1
.dockerignore Symbolic link
View file

@ -0,0 +1 @@
.gitignore

View file

@ -1,7 +1,5 @@
# name: Publish Docker Images
name: Create and publish a Docker image
# Configures this workflow to run every time a change is pushed to the branch called `release`.
on: on:
push: push:
tags: [ 'v*.*.*' ] tags: [ 'v*.*.*' ]
@ -9,12 +7,20 @@ on:
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. # Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env: env:
REGISTRY: ghcr.io REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }} # This also contains the owner, i.e. tun2proxy/tun2proxy.
IMAGE_PATH: ${{ github.repository }}
IMAGE_NAME: ${{ github.event.repository.name }}
DEFAULT_OS: scratch
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. # There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs: jobs:
build-and-push-image: build-and-push-image:
name: Build and push Docker image
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ 'scratch', 'ubuntu', 'alpine' ]
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions: permissions:
contents: read contents: read
@ -31,30 +37,36 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 uses: docker/login-action@v3
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker Image
id: meta id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 uses: docker/metadata-action@v5
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # We publish the images with an OS-suffix.
# The image based on a default OS is also published without a suffix.
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_PATH }}-${{ matrix.os }}
${{ env.DEFAULT_OS == matrix.os && format('{0}/{1}', env.REGISTRY, env.IMAGE_PATH) || '' }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 uses: docker/build-push-action@v6
with: with:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
context: . context: .
file: Dockerfile
target: ${{ env.IMAGE_NAME }}-${{ matrix.os }}
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

View file

@ -8,6 +8,8 @@ on:
pull_request: pull_request:
branches: branches:
- '**' - '**'
schedule:
- cron: '0 0 * * 0' # Every Sunday at midnight UTC
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always

View file

@ -19,7 +19,9 @@ jobs:
- name: Populate .env - name: Populate .env
env: env:
DOTENV: ${{ secrets.DOTENV }} DOTENV: ${{ secrets.DOTENV }}
run: echo "$DOTENV" > .env run: |
echo "$DOTENV" > tests/.env
ln -s tests/.env
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
@ -36,7 +38,7 @@ jobs:
- name: Build project - name: Build project
run: cargo build --release run: cargo build --release
- name: Run tests - name: Run tests
run: | run: |
source venv/bin/activate source venv/bin/activate

11
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,11 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/rhysd/actionlint
rev: v1.7.7
hooks:
- id: actionlint

View file

@ -1,6 +1,6 @@
[package] [package]
name = "tun2proxy" name = "tun2proxy"
version = "0.7.9" version = "0.7.11"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://github.com/tun2proxy/tun2proxy" repository = "https://github.com/tun2proxy/tun2proxy"
@ -31,7 +31,7 @@ async-trait = "0.1"
base64easy = "0.1" base64easy = "0.1"
chrono = "0.4" chrono = "0.4"
clap = { version = "4", features = ["derive", "wrap_help", "color"] } clap = { version = "4", features = ["derive", "wrap_help", "color"] }
ctrlc2 = { version = "3", features = ["tokio", "termination"] } ctrlc2 = { version = "3.6.5", features = ["async", "termination"] }
digest_auth = "0.3" digest_auth = "0.3"
dotenvy = "0.15" dotenvy = "0.15"
env_logger = "0.11" env_logger = "0.11"
@ -49,24 +49,23 @@ socks5-impl = { version = "0.7", default-features = false, features = [
thiserror = "2" thiserror = "2"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
tokio-util = "0.7" tokio-util = "0.7"
tproxy-config = { version = "6", default-features = false } tproxy-config = { version = "7", default-features = false }
tun = { version = "0.7", features = ["async"] } tun = { version = "0.8", features = ["async"] }
udp-stream = { version = "0.0.12", default-features = false } udp-stream = { version = "0.0.12", default-features = false }
unicase = "2" unicase = "2"
url = "2" url = "2"
[build-dependencies]
chrono = "0.4"
serde_json = "1"
[target.'cfg(target_os="linux")'.dependencies]
serde = { version = "1", features = ["derive"] }
bincode = "2"
[target.'cfg(target_os="android")'.dependencies] [target.'cfg(target_os="android")'.dependencies]
android_logger = "0.15" android_logger = "0.15"
jni = { version = "0.21", default-features = false } jni = { version = "0.21", default-features = false }
[target.'cfg(target_os="linux")'.dependencies]
bincode = "2"
serde = { version = "1", features = ["derive"] }
[target.'cfg(target_os="windows")'.dependencies]
windows-service = "0.8"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
daemonize = "0.5" daemonize = "0.5"
nix = { version = "0.30", default-features = false, features = [ nix = { version = "0.30", default-features = false, features = [
@ -75,8 +74,9 @@ nix = { version = "0.30", default-features = false, features = [
"uio", "uio",
] } ] }
[target.'cfg(target_os = "windows")'.dependencies] [build-dependencies]
windows-service = "0.8" chrono = "0.4"
serde_json = "1"
# [profile.release] # [profile.release]
# strip = "symbols" # strip = "symbols"

View file

@ -1,20 +1,61 @@
#################################################################################################### ####################################################################################################
## Builder # This is a multi-stage Dockerfile.
# Build with `docker buildx build -t <image-tag> --target <stage> .`
# For example, to build the Alpine-based image while naming it tun2proxy, run:
# `docker buildx build -t tun2proxy --target tun2proxy-alpine .`
#################################################################################################### ####################################################################################################
FROM rust:latest AS builder
WORKDIR /worker
COPY ./ .
RUN cargo build --release
#################################################################################################### ####################################################################################################
## Final image ## glibc builder
#################################################################################################### ####################################################################################################
FROM ubuntu:latest FROM rust:latest AS glibc-builder
RUN apt update && apt install -y iproute2 && apt clean all WORKDIR /worker
COPY ./ .
RUN cargo build --release
COPY --from=builder /worker/target/release/tun2proxy-bin /usr/bin/tun2proxy-bin ####################################################################################################
## musl builder
####################################################################################################
FROM rust:latest AS musl-builder
ENTRYPOINT ["/usr/bin/tun2proxy-bin", "--setup"] WORKDIR /worker
COPY ./ .
RUN ARCH=$(rustc -vV | sed -nE 's/host:\s*([^-]+).*/\1/p') \
&& rustup target add "$ARCH-unknown-linux-musl" \
&& cargo build --release --target "$ARCH-unknown-linux-musl"
RUN mkdir /.etc \
&& touch /.etc/resolv.conf \
&& mkdir /.tmp \
&& chmod 777 /.tmp \
&& chmod +t /.tmp
####################################################################################################
## Alpine image
####################################################################################################
FROM alpine:latest AS tun2proxy-alpine
COPY --from=musl-builder /worker/target/*/release/tun2proxy-bin /usr/bin/tun2proxy-bin
ENTRYPOINT ["/usr/bin/tun2proxy-bin", "--setup"]
####################################################################################################
## Ubuntu image
####################################################################################################
FROM ubuntu:latest AS tun2proxy-ubuntu
COPY --from=glibc-builder /worker/target/release/tun2proxy-bin /usr/bin/tun2proxy-bin
ENTRYPOINT ["/usr/bin/tun2proxy-bin", "--setup"]
####################################################################################################
## OS-less image (default)
####################################################################################################
FROM scratch AS tun2proxy-scratch
COPY --from=musl-builder ./tmp /tmp
COPY --from=musl-builder ./etc /etc
COPY --from=musl-builder /worker/target/*/release/tun2proxy-bin /usr/bin/tun2proxy-bin
ENTRYPOINT ["/usr/bin/tun2proxy-bin", "--setup"]

View file

@ -29,7 +29,7 @@ cargo build --release
``` ```
### Building Framework for Apple Devices ### Building Framework for Apple Devices
To build an XCFramework for macOS and iOS, run the following: To build an XCFramework for macOS and iOS, run the following:
``` ```
./build-apple.sh ./build-apple.sh
``` ```
@ -149,8 +149,8 @@ Options:
--unshare-pidfile <UNSHARE_PIDFILE> Create a pidfile of `unshare` process when using `--unshare` --unshare-pidfile <UNSHARE_PIDFILE> Create a pidfile of `unshare` process when using `--unshare`
-6, --ipv6-enabled IPv6 enabled -6, --ipv6-enabled IPv6 enabled
-s, --setup Routing and system setup, which decides whether to setup the routing and system -s, --setup Routing and system setup, which decides whether to setup the routing and system
configuration. This option is only available on Linux and requires root-like privileges. configuration. This option requires root-like privileges on every platform.
See `capabilities(7)` It is very important on Linux, see `capabilities(7)`
-d, --dns <strategy> DNS handling strategy [default: direct] [possible values: virtual, over-tcp, direct] -d, --dns <strategy> DNS handling strategy [default: direct] [possible values: virtual, over-tcp, direct]
--dns-addr <IP> DNS resolver address [default: 8.8.8.8] --dns-addr <IP> DNS resolver address [default: 8.8.8.8]
--virtual-dns-pool <CIDR> IP address pool to be used by virtual DNS in CIDR notation [default: 198.18.0.0/15] --virtual-dns-pool <CIDR> IP address pool to be used by virtual DNS in CIDR notation [default: 198.18.0.0/15]
@ -177,7 +177,16 @@ supplied as `--proxy http://john.doe:secret@1.2.3.4:3128`. This works analogousl
Tun2proxy can serve as a proxy for other Docker containers. To make use of that feature, first build the image: Tun2proxy can serve as a proxy for other Docker containers. To make use of that feature, first build the image:
```bash ```bash
docker build -t tun2proxy . docker buildx build -t tun2proxy .
```
This will build an image containing a statically linked `tun2proxy` binary (based on `musl`) without OS.
Alternatively, you can build images based on Ubuntu or Alpine as follows:
```bash
docker buildx build -t tun2proxy --target tun2proxy-ubuntu .
docker buildx build -t tun2proxy --target tun2proxy-alpine .
``` ```
Next, start a container from the tun2proxy image: Next, start a container from the tun2proxy image:
@ -188,7 +197,7 @@ docker run -d \
--sysctl net.ipv6.conf.default.disable_ipv6=0 \ --sysctl net.ipv6.conf.default.disable_ipv6=0 \
--cap-add NET_ADMIN \ --cap-add NET_ADMIN \
--name tun2proxy \ --name tun2proxy \
tun2proxy-bin --proxy proto://[username[:password]@]host:port tun2proxy --proxy proto://[username[:password]@]host:port
``` ```
You can then provide the running container's network to another worker container by sharing the network namespace (like kubernetes sidecar): You can then provide the running container's network to another worker container by sharing the network namespace (like kubernetes sidecar):
@ -200,7 +209,7 @@ docker run -it \
``` ```
### Docker Compose ### Docker Compose
Write a `docker-compose.yaml` file with the following content: Create a `docker-compose.yaml` file with the following content:
```yaml ```yaml
services: services:
@ -212,7 +221,7 @@ services:
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
container_name: tun2proxy container_name: tun2proxy
image: ghcr.io/tun2proxy/tun2proxy:latest image: ghcr.io/tun2proxy/tun2proxy-ubuntu:latest
command: --proxy proto://[username[:password]@]host:port command: --proxy proto://[username[:password]@]host:port
alpine: alpine:
stdin_open: true stdin_open: true

View file

@ -6,7 +6,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Get the build time // Get the build time
let build_time = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); let build_time = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
println!("cargo:rustc-env=BUILD_TIME={}", build_time); println!("cargo:rustc-env=BUILD_TIME={build_time}");
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
if let Ok(cargo_target_dir) = get_cargo_target_dir() { if let Ok(cargo_target_dir) = get_cargo_target_dir() {
@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Copy to the target directory // Copy to the target directory
if let Err(e) = std::fs::copy(src_path, &dst_path) { if let Err(e) = std::fs::copy(src_path, &dst_path) {
f.write_all(format!("Failed to copy 'wintun.dll': {}\r\n", e).as_bytes())?; f.write_all(format!("Failed to copy 'wintun.dll': {e}\r\n").as_bytes())?;
} else { } else {
f.write_all(format!("Copied 'wintun.dll' to '{}'\r\n", dst_path.display()).as_bytes())?; f.write_all(format!("Copied 'wintun.dll' to '{}'\r\n", dst_path.display()).as_bytes())?;

View file

@ -76,8 +76,9 @@ pub struct Args {
pub ipv6_enabled: bool, pub ipv6_enabled: bool,
/// Routing and system setup, which decides whether to setup the routing and system configuration. /// Routing and system setup, which decides whether to setup the routing and system configuration.
/// This option is only available on Linux and requires root-like privileges. See `capabilities(7)`. /// This option requires root-like privileges on every platform.
#[arg(short, long, default_value = if cfg!(target_os = "linux") { "false" } else { "true" })] /// It is very important on Linux, see `capabilities(7)`.
#[arg(short, long)]
pub setup: bool, pub setup: bool,
/// DNS handling strategy /// DNS handling strategy
@ -378,7 +379,7 @@ impl Default for ArgProxy {
impl std::fmt::Display for ArgProxy { impl std::fmt::Display for ArgProxy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let auth = match &self.credentials { let auth = match &self.credentials {
Some(creds) => format!("{}", creds), Some(creds) => format!("{creds}"),
None => "".to_owned(), None => "".to_owned(),
}; };
if auth.is_empty() { if auth.is_empty() {

View file

@ -27,13 +27,20 @@ fn main() -> Result<(), BoxError> {
rt.block_on(main_async(args)) rt.block_on(main_async(args))
} }
async fn main_async(args: Args) -> Result<(), BoxError> { fn setup_logging(args: &Args) {
let ipstack = match args.verbosity { let avoid_trace = match args.verbosity {
ArgVerbosity::Trace => ArgVerbosity::Debug, ArgVerbosity::Trace => ArgVerbosity::Debug,
_ => args.verbosity, _ => args.verbosity,
}; };
let default = format!("{:?},hickory_proto=warn,ipstack={:?}", args.verbosity, ipstack); let default = format!(
"{:?},hickory_proto=warn,ipstack={:?},netlink_proto={:?},netlink_sys={:?}",
args.verbosity, avoid_trace, avoid_trace, avoid_trace
);
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init(); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init();
}
async fn main_async(args: Args) -> Result<(), BoxError> {
setup_logging(&args);
let shutdown_token = tokio_util::sync::CancellationToken::new(); let shutdown_token = tokio_util::sync::CancellationToken::new();
let main_loop_handle = tokio::spawn({ let main_loop_handle = tokio::spawn({
@ -43,7 +50,7 @@ async fn main_async(args: Args) -> Result<(), BoxError> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if args.unshare && args.socket_transfer_fd.is_none() { if args.unshare && args.socket_transfer_fd.is_none() {
if let Err(err) = namespace_proxy_main(args, shutdown_token).await { if let Err(err) = namespace_proxy_main(args, shutdown_token).await {
log::error!("namespace proxy error: {}", err); log::error!("namespace proxy error: {err}");
} }
return Ok(0); return Ok(0);
} }
@ -64,18 +71,18 @@ async fn main_async(args: Args) -> Result<(), BoxError> {
let ctrlc_fired = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); let ctrlc_fired = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let ctrlc_fired_clone = ctrlc_fired.clone(); let ctrlc_fired_clone = ctrlc_fired.clone();
let ctrlc_handel = ctrlc2::set_async_handler(async move { let ctrlc_handel = ctrlc2::AsyncCtrlC::new(move || {
log::info!("Ctrl-C received, exiting..."); log::info!("Ctrl-C received, exiting...");
ctrlc_fired_clone.store(true, std::sync::atomic::Ordering::SeqCst); ctrlc_fired_clone.store(true, std::sync::atomic::Ordering::SeqCst);
shutdown_token.cancel(); shutdown_token.cancel();
}) true
.await; })?;
let tasks = main_loop_handle.await??; let tasks = main_loop_handle.await??;
if ctrlc_fired.load(std::sync::atomic::Ordering::SeqCst) { if ctrlc_fired.load(std::sync::atomic::Ordering::SeqCst) {
log::info!("Ctrl-C fired, waiting the handler to finish..."); log::info!("Ctrl-C fired, waiting the handler to finish...");
ctrlc_handel.await.map_err(|err| err.to_string())?; ctrlc_handel.await?;
} }
if args.exit_on_fatal_error && tasks >= args.max_sessions { if args.exit_on_fatal_error && tasks >= args.max_sessions {
@ -126,13 +133,10 @@ async fn namespace_proxy_main(
log::info!("Use `tun2proxy-bin --unshare --setup [...] -- openvpn --config [...]`"); log::info!("Use `tun2proxy-bin --unshare --setup [...] -- openvpn --config [...]`");
log::info!(""); log::info!("");
log::info!("To run a new process in the created namespace (e.g. a flatpak app)"); log::info!("To run a new process in the created namespace (e.g. a flatpak app)");
log::info!( log::info!("Use `nsenter --preserve-credentials --user --net --mount --target {unshare_pid} /bin/sh`");
"Use `nsenter --preserve-credentials --user --net --mount --target {} /bin/sh`",
unshare_pid
);
log::info!(""); log::info!("");
if let Some(pidfile) = _args.unshare_pidfile.as_ref() { if let Some(pidfile) = _args.unshare_pidfile.as_ref() {
log::info!("Writing unshare pid to {}", pidfile); log::info!("Writing unshare pid to {pidfile}");
std::fs::write(pidfile, unshare_pid.to_string()).ok(); std::fs::write(pidfile, unshare_pid.to_string()).ok();
} }
tokio::spawn(async move { tun2proxy::socket_transfer::process_socket_requests(&socket).await }); tokio::spawn(async move { tun2proxy::socket_transfer::process_socket_requests(&socket).await });

View file

@ -66,14 +66,14 @@ impl UdpGwArgs {
async fn send_error_response(tx: Sender<Packet>, conn_id: u16) { async fn send_error_response(tx: Sender<Packet>, conn_id: u16) {
let error_packet = Packet::build_error_packet(conn_id); let error_packet = Packet::build_error_packet(conn_id);
if let Err(e) = tx.send(error_packet).await { if let Err(e) = tx.send(error_packet).await {
log::error!("send error response error {:?}", e); log::error!("send error response error {e:?}");
} }
} }
async fn send_keepalive_response(tx: Sender<Packet>, conn_id: u16) { async fn send_keepalive_response(tx: Sender<Packet>, conn_id: u16) {
let keepalive_packet = Packet::build_keepalive_packet(conn_id); let keepalive_packet = Packet::build_keepalive_packet(conn_id);
if let Err(e) = tx.send(keepalive_packet).await { if let Err(e) = tx.send(keepalive_packet).await {
log::error!("send keepalive response error {:?}", e); log::error!("send keepalive response error {e:?}");
} }
} }
@ -150,12 +150,12 @@ async fn process_client_udp_req(args: &UdpGwArgs, tx: Sender<Packet>, mut client
let packet = match res { let packet = match res {
Ok(Ok(packet)) => packet, Ok(Ok(packet)) => packet,
Ok(Err(e)) => { Ok(Err(e)) => {
log::debug!("client {} retrieve_from_async_stream \"{}\"", masked_addr, e); log::debug!("client {masked_addr} retrieve_from_async_stream \"{e}\"");
break; break;
} }
Err(e) => { Err(e) => {
if client.last_activity.elapsed() >= CLIENT_DISCONNECT_TIMEOUT { if client.last_activity.elapsed() >= CLIENT_DISCONNECT_TIMEOUT {
log::debug!("client {} last_activity elapsed \"{e}\"", masked_addr); log::debug!("client {masked_addr} last_activity elapsed \"{e}\"");
break; break;
} }
continue; continue;
@ -166,19 +166,19 @@ async fn process_client_udp_req(args: &UdpGwArgs, tx: Sender<Packet>, mut client
let flags = packet.header.flags; let flags = packet.header.flags;
let conn_id = packet.header.conn_id; let conn_id = packet.header.conn_id;
if flags & UdpFlag::KEEPALIVE == UdpFlag::KEEPALIVE { if flags & UdpFlag::KEEPALIVE == UdpFlag::KEEPALIVE {
log::trace!("client {} send keepalive", masked_addr); log::trace!("client {masked_addr} send keepalive");
// 2. if keepalive packet, do nothing, send keepalive response to client // 2. if keepalive packet, do nothing, send keepalive response to client
send_keepalive_response(tx.clone(), conn_id).await; send_keepalive_response(tx.clone(), conn_id).await;
continue; continue;
} }
log::trace!("client {} received udp data {}", masked_addr, packet); log::trace!("client {masked_addr} received udp data {packet}");
// 3. process client udpgw packet in a new task // 3. process client udpgw packet in a new task
let tx = tx.clone(); let tx = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = process_udp(udp_mtu, udp_timeout, tx.clone(), packet).await { if let Err(e) = process_udp(udp_mtu, udp_timeout, tx.clone(), packet).await {
send_error_response(tx, conn_id).await; send_error_response(tx, conn_id).await;
log::debug!("client {} process udp function \"{e}\"", masked_addr); log::debug!("client {masked_addr} process udp function \"{e}\"");
} }
}); });
} }
@ -190,7 +190,7 @@ async fn write_to_client(addr: SocketAddr, mut writer: WriteHalf<'_>, mut rx: Re
loop { loop {
use std::io::{Error, ErrorKind::BrokenPipe}; use std::io::{Error, ErrorKind::BrokenPipe};
let packet = rx.recv().await.ok_or(Error::new(BrokenPipe, "recv error"))?; let packet = rx.recv().await.ok_or(Error::new(BrokenPipe, "recv error"))?;
log::trace!("send response to client {} with {}", masked_addr, packet); log::trace!("send response to client {masked_addr} with {packet}");
let data: Vec<u8> = packet.into(); let data: Vec<u8> = packet.into();
let _r = writer.write(&data).await?; let _r = writer.write(&data).await?;
} }
@ -205,18 +205,18 @@ async fn main_async(args: UdpGwArgs) -> Result<(), BoxError> {
let ctrlc_fired = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); let ctrlc_fired = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let ctrlc_fired_clone = ctrlc_fired.clone(); let ctrlc_fired_clone = ctrlc_fired.clone();
let ctrlc_handel = ctrlc2::set_async_handler(async move { let ctrlc_handel = ctrlc2::AsyncCtrlC::new(move || {
log::info!("Ctrl-C received, exiting..."); log::info!("Ctrl-C received, exiting...");
ctrlc_fired_clone.store(true, std::sync::atomic::Ordering::SeqCst); ctrlc_fired_clone.store(true, std::sync::atomic::Ordering::SeqCst);
shutdown_token.cancel(); shutdown_token.cancel();
}) true
.await; })?;
let _ = main_loop_handle.await?; let _ = main_loop_handle.await?;
if ctrlc_fired.load(std::sync::atomic::Ordering::SeqCst) { if ctrlc_fired.load(std::sync::atomic::Ordering::SeqCst) {
log::info!("Ctrl-C fired, waiting the handler to finish..."); log::info!("Ctrl-C fired, waiting the handler to finish...");
ctrlc_handel.await.map_err(|err| err.to_string())?; ctrlc_handel.await?;
} }
Ok(()) Ok(())
@ -231,7 +231,7 @@ pub async fn run(args: UdpGwArgs, shutdown_token: tokio_util::sync::Cancellation
}; };
let client = Client::new(addr); let client = Client::new(addr);
let masked_addr = mask_socket_addr(addr); let masked_addr = mask_socket_addr(addr);
log::info!("client {} connected", masked_addr); log::info!("client {masked_addr} connected");
let params = args.clone(); let params = args.clone();
tokio::spawn(async move { tokio::spawn(async move {
let (tx, rx) = tokio::sync::mpsc::channel::<Packet>(100); let (tx, rx) = tokio::sync::mpsc::channel::<Packet>(100);
@ -240,7 +240,7 @@ pub async fn run(args: UdpGwArgs, shutdown_token: tokio_util::sync::Cancellation
v = process_client_udp_req(&params, tx, client, tcp_read_stream) => v, v = process_client_udp_req(&params, tx, client, tcp_read_stream) => v,
v = write_to_client(addr, tcp_write_stream, rx) => v, v = write_to_client(addr, tcp_write_stream, rx) => v,
}; };
log::info!("client {} disconnected with {:?}", masked_addr, res); log::info!("client {masked_addr} disconnected with {res:?}");
}); });
} }
Ok::<(), Error>(()) Ok::<(), Error>(())
@ -263,9 +263,7 @@ fn main() -> Result<(), BoxError> {
.stdout(stdout) .stdout(stdout)
.stderr(stderr) .stderr(stderr)
.privileged_action(|| "Executed before drop privileges"); .privileged_action(|| "Executed before drop privileges");
let _ = daemonize let _ = daemonize.start().map_err(|e| format!("Failed to daemonize process, error:{e:?}"))?;
.start()
.map_err(|e| format!("Failed to daemonize process, error:{:?}", e))?;
} }
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?; let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;

View file

@ -23,7 +23,7 @@ pub enum Error {
TryFromSlice(#[from] std::array::TryFromSliceError), TryFromSlice(#[from] std::array::TryFromSliceError),
#[error("IpStackError {0:?}")] #[error("IpStackError {0:?}")]
IpStack(#[from] ipstack::IpStackError), IpStack(#[from] Box<ipstack::IpStackError>),
#[error("DnsProtoError {0:?}")] #[error("DnsProtoError {0:?}")]
DnsProto(#[from] hickory_proto::ProtoError), DnsProto(#[from] hickory_proto::ProtoError),
@ -45,6 +45,12 @@ pub enum Error {
IntParseError(#[from] std::num::ParseIntError), IntParseError(#[from] std::num::ParseIntError),
} }
impl From<ipstack::IpStackError> for Error {
fn from(err: ipstack::IpStackError) -> Self {
Self::IpStack(Box::new(err))
}
}
impl From<&str> for Error { impl From<&str> for Error {
fn from(err: &str) -> Self { fn from(err: &str) -> Self {
Self::String(err.to_string()) Self::String(err.to_string())
@ -67,7 +73,7 @@ impl From<Error> for std::io::Error {
fn from(err: Error) -> Self { fn from(err: Error) -> Self {
match err { match err {
Error::Io(err) => err, Error::Io(err) => err,
_ => std::io::Error::new(std::io::ErrorKind::Other, err), _ => std::io::Error::other(err),
} }
} }
} }

View file

@ -100,7 +100,7 @@ pub unsafe extern "C" fn tun2proxy_run_with_cli_args(cli_args: *const c_char, tu
pub fn general_run_for_api(args: Args, tun_mtu: u16, packet_information: bool) -> c_int { pub fn general_run_for_api(args: Args, tun_mtu: u16, packet_information: bool) -> c_int {
log::set_max_level(args.verbosity.into()); log::set_max_level(args.verbosity.into());
if let Err(err) = log::set_boxed_logger(Box::<crate::dump_logger::DumpLogger>::default()) { if let Err(err) = log::set_boxed_logger(Box::<crate::dump_logger::DumpLogger>::default()) {
log::debug!("set logger error: {}", err); log::debug!("set logger error: {err}");
} }
let shutdown_token = tokio_util::sync::CancellationToken::new(); let shutdown_token = tokio_util::sync::CancellationToken::new();
@ -135,7 +135,7 @@ pub fn general_run_for_api(args: Args, tun_mtu: u16, packet_information: bool) -
}) { }) {
Ok(_) => 0, Ok(_) => 0,
Err(e) => { Err(e) => {
log::error!("failed to run tun2proxy with error: {:?}", e); log::error!("failed to run tun2proxy with error: {e:?}");
-4 -4
} }
} }
@ -196,9 +196,6 @@ pub async fn general_run_async(
.bypass_ips(&args.bypass) .bypass_ips(&args.bypass)
.ipv6_default_route(args.ipv6_enabled); .ipv6_default_route(args.ipv6_enabled);
#[allow(unused_mut, unused_assignments, unused_variables)]
let mut setup = true;
let device = tun::create_as_async(&tun_config)?; let device = tun::create_as_async(&tun_config)?;
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
@ -210,16 +207,11 @@ pub async fn general_run_async(
// TproxyState implements the Drop trait to restore network configuration, // TproxyState implements the Drop trait to restore network configuration,
// so we need to assign it to a variable, even if it is not used. // so we need to assign it to a variable, even if it is not used.
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
let mut _restore: Option<tproxy_config::TproxyState> = None; let mut restore: Option<tproxy_config::TproxyState> = None;
#[cfg(target_os = "linux")]
{
setup = args.setup;
}
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
if setup { if args.setup {
_restore = Some(tproxy_config::tproxy_setup(&tproxy_args)?); restore = Some(tproxy_config::tproxy_setup(&tproxy_args).await?);
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -246,8 +238,16 @@ pub async fn general_run_async(
} }
} }
let join_handle = tokio::spawn(crate::run(device, tun_mtu, args, shutdown_token)); let join_handle = tokio::spawn(crate::run(device, tun_mtu, args, shutdown_token.clone()));
Ok(join_handle.await.map_err(std::io::Error::from)??)
match join_handle.await? {
Ok(sessions) => {
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
tproxy_config::tproxy_remove(restore).await?;
Ok(sessions)
}
Err(err) => Err(std::io::Error::from(err)),
}
} }
/// # Safety /// # Safety

View file

@ -142,7 +142,7 @@ impl HttpConnection {
AuthenticationScheme::Basic => { AuthenticationScheme::Basic => {
let auth_b64 = base64easy::encode(credentials.to_string(), base64easy::EngineKind::Standard); let auth_b64 = base64easy::encode(credentials.to_string(), base64easy::EngineKind::Standard);
self.server_outbuf self.server_outbuf
.extend(format!("{}: Basic {}\r\n", PROXY_AUTHORIZATION, auth_b64).as_bytes()); .extend(format!("{PROXY_AUTHORIZATION}: Basic {auth_b64}\r\n").as_bytes());
} }
AuthenticationScheme::None => {} AuthenticationScheme::None => {}
} }

View file

@ -237,7 +237,7 @@ where
#[cfg(feature = "udpgw")] #[cfg(feature = "udpgw")]
let udpgw_client = args.udpgw_server.map(|addr| { let udpgw_client = args.udpgw_server.map(|addr| {
log::info!("UDP Gateway enabled, server: {}", addr); log::info!("UDP Gateway enabled, server: {addr}");
use std::time::Duration; use std::time::Duration;
let client = Arc::new(UdpGwClient::new( let client = Arc::new(UdpGwClient::new(
mtu, mtu,
@ -292,7 +292,7 @@ where
let socket_queue = socket_queue.clone(); let socket_queue = socket_queue.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = handle_tcp_session(tcp, proxy_handler, socket_queue).await { if let Err(err) = handle_tcp_session(tcp, proxy_handler, socket_queue).await {
log::error!("{} error \"{}\"", info, err); log::error!("{info} error \"{err}\"");
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1));
}); });
@ -318,7 +318,7 @@ where
let socket_queue = socket_queue.clone(); let socket_queue = socket_queue.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = handle_dns_over_tcp_session(udp, proxy_handler, socket_queue, ipv6_enabled).await { if let Err(err) = handle_dns_over_tcp_session(udp, proxy_handler, socket_queue, ipv6_enabled).await {
log::error!("{} error \"{}\"", info, err); log::error!("{info} error \"{err}\"");
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1));
}); });
@ -328,7 +328,7 @@ where
tokio::spawn(async move { tokio::spawn(async move {
if let Some(virtual_dns) = virtual_dns { if let Some(virtual_dns) = virtual_dns {
if let Err(err) = handle_virtual_dns_session(udp, virtual_dns).await { if let Err(err) = handle_virtual_dns_session(udp, virtual_dns).await {
log::error!("{} error \"{}\"", info, err); log::error!("{info} error \"{err}\"");
} }
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1));
@ -360,7 +360,7 @@ where
None => dst.into(), None => dst.into(),
}; };
if let Err(e) = handle_udp_gateway_session(udp, udpgw, &dst_addr, proxy_handler, queue, ipv6_enabled).await { if let Err(e) = handle_udp_gateway_session(udp, udpgw, &dst_addr, proxy_handler, queue, ipv6_enabled).await {
log::info!("Ending {} with \"{}\"", info, e); log::info!("Ending {info} with \"{e}\"");
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1));
}); });
@ -372,13 +372,13 @@ where
tokio::spawn(async move { tokio::spawn(async move {
let ty = args.proxy.proxy_type; let ty = args.proxy.proxy_type;
if let Err(err) = handle_udp_associate_session(udp, ty, proxy_handler, socket_queue, ipv6_enabled).await { if let Err(err) = handle_udp_associate_session(udp, ty, proxy_handler, socket_queue, ipv6_enabled).await {
log::info!("Ending {} with \"{}\"", info, err); log::info!("Ending {info} with \"{err}\"");
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1));
}); });
} }
Err(e) => { Err(e) => {
log::error!("Failed to create UDP connection: {}", e); log::error!("Failed to create UDP connection: {e}");
} }
} }
} }
@ -402,7 +402,7 @@ async fn handle_virtual_dns_session(mut udp: IpStackUdpStream, dns: Arc<Mutex<Vi
let len = match udp.read(&mut buf).await { let len = match udp.read(&mut buf).await {
Err(e) => { Err(e) => {
// indicate UDP read fails not an error. // indicate UDP read fails not an error.
log::debug!("Virtual DNS session error: {}", e); log::debug!("Virtual DNS session error: {e}");
break; break;
} }
Ok(len) => len, Ok(len) => len,
@ -412,7 +412,7 @@ async fn handle_virtual_dns_session(mut udp: IpStackUdpStream, dns: Arc<Mutex<Vi
} }
let (msg, qname, ip) = dns.lock().await.generate_query(&buf[..len])?; let (msg, qname, ip) = dns.lock().await.generate_query(&buf[..len])?;
udp.write_all(&msg).await?; udp.write_all(&msg).await?;
log::debug!("Virtual DNS query: {} -> {}", qname, ip); log::debug!("Virtual DNS query: {qname} -> {ip}");
} }
Ok(()) Ok(())
} }
@ -431,7 +431,7 @@ where
total += n as u64; total += n as u64;
let (tx, rx) = if is_tx { (n, 0) } else { (0, n) }; let (tx, rx) = if is_tx { (n, 0) } else { (0, n) };
if let Err(e) = crate::traffic_status::traffic_status_update(tx, rx) { if let Err(e) = crate::traffic_status::traffic_status_update(tx, rx) {
log::debug!("Record traffic status error: {}", e); log::debug!("Record traffic status error: {e}");
} }
writer.write_all(&buf[..n]).await?; writer.write_all(&buf[..n]).await?;
} }
@ -453,7 +453,7 @@ async fn handle_tcp_session(
let mut server = create_tcp_stream(&socket_queue, server_addr).await?; let mut server = create_tcp_stream(&socket_queue, server_addr).await?;
log::info!("Beginning {}", session_info); log::info!("Beginning {session_info}");
if let Err(e) = handle_proxy_session(&mut server, proxy_handler).await { if let Err(e) = handle_proxy_session(&mut server, proxy_handler).await {
tcp_stack.shutdown().await?; tcp_stack.shutdown().await?;
@ -467,19 +467,19 @@ async fn handle_tcp_session(
async move { async move {
let r = copy_and_record_traffic(&mut t_rx, &mut s_tx, true).await; let r = copy_and_record_traffic(&mut t_rx, &mut s_tx, true).await;
if let Err(err) = s_tx.shutdown().await { if let Err(err) = s_tx.shutdown().await {
log::trace!("{} s_tx shutdown error {}", session_info, err); log::trace!("{session_info} s_tx shutdown error {err}");
} }
r r
}, },
async move { async move {
let r = copy_and_record_traffic(&mut s_rx, &mut t_tx, false).await; let r = copy_and_record_traffic(&mut s_rx, &mut t_tx, false).await;
if let Err(err) = t_tx.shutdown().await { if let Err(err) = t_tx.shutdown().await {
log::trace!("{} t_tx shutdown error {}", session_info, err); log::trace!("{session_info} t_tx shutdown error {err}");
} }
r r
}, },
); );
log::info!("Ending {} with {:?}", session_info, res); log::info!("Ending {session_info} with {res:?}");
Ok(()) Ok(())
} }
@ -509,7 +509,7 @@ async fn handle_udp_gateway_session(
None => { None => {
let mut tcp_server_stream = create_tcp_stream(&socket_queue, proxy_server_addr).await?; let mut tcp_server_stream = create_tcp_stream(&socket_queue, proxy_server_addr).await?;
if let Err(e) = handle_proxy_session(&mut tcp_server_stream, proxy_handler).await { if let Err(e) = handle_proxy_session(&mut tcp_server_stream, proxy_handler).await {
return Err(format!("udpgw connection error: {}", e).into()); return Err(format!("udpgw connection error: {e}").into());
} }
break UdpGwClientStream::new(tcp_server_stream); break UdpGwClientStream::new(tcp_server_stream);
} }
@ -625,7 +625,7 @@ async fn handle_udp_associate_session(
) )
}; };
log::info!("Beginning {}", session_info); log::info!("Beginning {session_info}");
// `_server` is meaningful here, it must be alive all the time // `_server` is meaningful here, it must be alive all the time
// to ensure that UDP transmission will not be interrupted accidentally. // to ensure that UDP transmission will not be interrupted accidentally.
@ -702,7 +702,7 @@ async fn handle_udp_associate_session(
} }
} }
log::info!("Ending {}", session_info); log::info!("Ending {session_info}");
Ok(()) Ok(())
} }
@ -721,7 +721,7 @@ async fn handle_dns_over_tcp_session(
let mut server = create_tcp_stream(&socket_queue, server_addr).await?; let mut server = create_tcp_stream(&socket_queue, server_addr).await?;
log::info!("Beginning {}", session_info); log::info!("Beginning {session_info}");
let _ = handle_proxy_session(&mut server, proxy_handler).await?; let _ = handle_proxy_session(&mut server, proxy_handler).await?;
@ -774,7 +774,7 @@ async fn handle_dns_over_tcp_session(
let name = dns::extract_domain_from_dns_message(&message)?; let name = dns::extract_domain_from_dns_message(&message)?;
let ip = dns::extract_ipaddr_from_dns_message(&message); let ip = dns::extract_ipaddr_from_dns_message(&message);
log::trace!("DNS over TCP query result: {} -> {:?}", name, ip); log::trace!("DNS over TCP query result: {name} -> {ip:?}");
if !ipv6_enabled { if !ipv6_enabled {
dns::remove_ipv6_entries(&mut message); dns::remove_ipv6_entries(&mut message);
@ -794,7 +794,7 @@ async fn handle_dns_over_tcp_session(
} }
} }
log::info!("Ending {}", session_info); log::info!("Ending {session_info}");
Ok(()) Ok(())
} }

View file

@ -16,7 +16,7 @@ impl std::fmt::Display for IpProtocol {
IpProtocol::Tcp => write!(f, "TCP"), IpProtocol::Tcp => write!(f, "TCP"),
IpProtocol::Udp => write!(f, "UDP"), IpProtocol::Udp => write!(f, "UDP"),
IpProtocol::Icmp => write!(f, "ICMP"), IpProtocol::Icmp => write!(f, "ICMP"),
IpProtocol::Other(v) => write!(f, "Other(0x{:02X})", v), IpProtocol::Other(v) => write!(f, "Other(0x{v:02X})"),
} }
} }
} }

View file

@ -157,8 +157,7 @@ where
let mut buf = [0_u8; REQUEST_BUFFER_SIZE]; let mut buf = [0_u8; REQUEST_BUFFER_SIZE];
let mut iov = [IoSliceMut::new(&mut buf[..])]; let mut iov = [IoSliceMut::new(&mut buf[..])];
let mut cmsg = Vec::with_capacity(cmsg_space::<RawFd>() * number as usize); let mut cmsg = vec![0; cmsg_space::<RawFd>() * number as usize];
let msg = recvmsg::<()>(socket.as_fd().as_raw_fd(), &mut iov, Some(&mut cmsg), MsgFlags::empty()); let msg = recvmsg::<()>(socket.as_fd().as_raw_fd(), &mut iov, Some(&mut cmsg), MsgFlags::empty());
let msg = match msg { let msg = match msg {

View file

@ -78,7 +78,7 @@ impl SocksProxyImpl {
} }
} }
SocketAddr::V6(addr) => { SocketAddr::V6(addr) => {
return Err(format!("SOCKS4 does not support IPv6: {}", addr).into()); return Err(format!("SOCKS4 does not support IPv6: {addr}").into());
} }
} }
self.server_outbuf.extend(ip_vec); self.server_outbuf.extend(ip_vec);
@ -136,7 +136,7 @@ impl SocksProxyImpl {
let response = handshake::Response::retrieve_from_stream(&mut self.server_inbuf.clone()); let response = handshake::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = response { if let Err(e) = response {
if e.kind() == std::io::ErrorKind::UnexpectedEof { if e.kind() == std::io::ErrorKind::UnexpectedEof {
log::trace!("receive_server_hello_socks5 needs more data \"{}\"...", e); log::trace!("receive_server_hello_socks5 needs more data \"{e}\"...");
return Ok(()); return Ok(());
} else { } else {
return Err(e); return Err(e);
@ -181,7 +181,7 @@ impl SocksProxyImpl {
let response = Response::retrieve_from_stream(&mut self.server_inbuf.clone()); let response = Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = response { if let Err(e) = response {
if e.kind() == std::io::ErrorKind::UnexpectedEof { if e.kind() == std::io::ErrorKind::UnexpectedEof {
log::trace!("receive_auth_data needs more data \"{}\"...", e); log::trace!("receive_auth_data needs more data \"{e}\"...");
return Ok(()); return Ok(());
} else { } else {
return Err(e); return Err(e);
@ -213,7 +213,7 @@ impl SocksProxyImpl {
let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone()); let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = response { if let Err(e) = response {
if e.kind() == std::io::ErrorKind::UnexpectedEof { if e.kind() == std::io::ErrorKind::UnexpectedEof {
log::trace!("receive_connection_status needs more data \"{}\"...", e); log::trace!("receive_connection_status needs more data \"{e}\"...");
return Ok(()); return Ok(());
} else { } else {
return Err(e); return Err(e);

View file

@ -51,7 +51,7 @@ static TIME_STAMP: LazyLock<Mutex<std::time::Instant>> = LazyLock::new(|| Mutex:
pub(crate) fn traffic_status_update(delta_tx: usize, delta_rx: usize) -> Result<()> { pub(crate) fn traffic_status_update(delta_tx: usize, delta_rx: usize) -> Result<()> {
{ {
let is_none_or_error = TRAFFIC_STATUS_CALLBACK.lock().map(|guard| guard.is_none()).unwrap_or_else(|e| { let is_none_or_error = TRAFFIC_STATUS_CALLBACK.lock().map(|guard| guard.is_none()).unwrap_or_else(|e| {
log::error!("Failed to acquire lock: {}", e); log::error!("Failed to acquire lock: {e}");
true true
}); });
if is_none_or_error { if is_none_or_error {

View file

@ -32,9 +32,9 @@ impl std::fmt::Display for UdpFlag {
0x01 => "KEEPALIVE", 0x01 => "KEEPALIVE",
0x20 => "ERR", 0x20 => "ERR",
0x02 => "DATA", 0x02 => "DATA",
n => return write!(f, "Unknown UdpFlag(0x{:02X})", n), n => return write!(f, "Unknown UdpFlag(0x{n:02X})"),
}; };
write!(f, "{}", flag) write!(f, "{flag}")
} }
} }
@ -332,7 +332,7 @@ impl std::fmt::Display for UdpGwResponse {
UdpGwResponse::KeepAlive => write!(f, "KeepAlive"), UdpGwResponse::KeepAlive => write!(f, "KeepAlive"),
UdpGwResponse::Error => write!(f, "Error"), UdpGwResponse::Error => write!(f, "Error"),
UdpGwResponse::TcpClose => write!(f, "TcpClose"), UdpGwResponse::TcpClose => write!(f, "TcpClose"),
UdpGwResponse::Data(packet) => write!(f, "Data({})", packet), UdpGwResponse::Data(packet) => write!(f, "Data({packet})"),
} }
} }
} }
@ -487,21 +487,21 @@ impl UdpGwClient {
let keepalive_packet: Vec<u8> = Packet::build_keepalive_packet(sn).into(); let keepalive_packet: Vec<u8> = Packet::build_keepalive_packet(sn).into();
tx += keepalive_packet.len(); tx += keepalive_packet.len();
if let Err(e) = stream_writer.write_all(&keepalive_packet).await { if let Err(e) = stream_writer.write_all(&keepalive_packet).await {
log::warn!("stream {} {:?} send keepalive failed: {}", sn, local_addr, e); log::warn!("stream {sn} {local_addr:?} send keepalive failed: {e}");
continue; continue;
} }
match UdpGwClient::recv_udpgw_packet(self.udp_mtu, self.udp_timeout, &mut stream_reader).await { match UdpGwClient::recv_udpgw_packet(self.udp_mtu, self.udp_timeout, &mut stream_reader).await {
Ok((len, UdpGwResponse::KeepAlive)) => { Ok((len, UdpGwResponse::KeepAlive)) => {
stream.update_activity(); stream.update_activity();
self.store_server_connection_full(stream, stream_reader, stream_writer).await; self.store_server_connection_full(stream, stream_reader, stream_writer).await;
log::trace!("stream {sn} {:?} send keepalive and recieve it successfully", local_addr); log::trace!("stream {sn} {local_addr:?} send keepalive and recieve it successfully");
rx += len; rx += len;
} }
Ok((len, v)) => { Ok((len, v)) => {
log::debug!("stream {sn} {:?} keepalive unexpected response: {v}", local_addr); log::debug!("stream {sn} {local_addr:?} keepalive unexpected response: {v}");
rx += len; rx += len;
} }
Err(e) => log::debug!("stream {sn} {:?} keepalive no response, error \"{e}\"", local_addr), Err(e) => log::debug!("stream {sn} {local_addr:?} keepalive no response, error \"{e}\""),
} }
} }
crate::traffic_status::traffic_status_update(tx, rx)?; crate::traffic_status::traffic_status_update(tx, rx)?;

View file

@ -16,7 +16,7 @@ fn my_service_main(arguments: Vec<std::ffi::OsString>) {
// `service_dispatcher::start` from `main`. // `service_dispatcher::start` from `main`.
if let Err(_e) = run_service(arguments) { if let Err(_e) = run_service(arguments) {
log::error!("Error: {:?}", _e); log::error!("Error: {_e:?}");
} }
} }