Compare commits

..

No commits in common. "master" and "v0.7.2" have entirely different histories.

21 changed files with 120 additions and 186 deletions

View file

@ -15,6 +15,6 @@ jobs:
- name: Auto approve pull request, then squash and merge - name: Auto approve pull request, then squash and merge
uses: ahmadnassri/action-dependabot-auto-merge@v2 uses: ahmadnassri/action-dependabot-auto-merge@v2
with: with:
# target: minor target: minor
# here `PAT_REPO_ADMIN` is a user's passkey provided by github. # here `PAT_REPO_ADMIN` is a user's passkey provided by github.
github-token: ${{ secrets.PAT_REPO_ADMIN }} github-token: ${{ secrets.PAT_REPO_ADMIN }}

View file

@ -34,7 +34,7 @@ jobs:
include: include:
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
host_os: ubuntu-22.04 host_os: ubuntu-latest
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
host_os: ubuntu-latest host_os: ubuntu-latest
- target: i686-unknown-linux-musl - target: i686-unknown-linux-musl
@ -73,7 +73,7 @@ jobs:
rustup target add ${{ matrix.target }} rustup target add ${{ matrix.target }}
fi fi
cargo install cbindgen cargo install cbindgen
if [[ "${{ contains(matrix.host_os, 'ubuntu') }}" == "true" && "${{ matrix.host_os }}" != "ubuntu-22.04" ]]; then if [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
sudo .github/workflows/install-cross.sh sudo .github/workflows/install-cross.sh
fi fi
@ -81,7 +81,7 @@ jobs:
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
shell: bash shell: bash
run: | run: |
if [[ "${{ contains(matrix.host_os, 'ubuntu') }}" == "true" && "${{ matrix.host_os }}" != "ubuntu-22.04" ]]; then if [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
cross build --all-features --release --target ${{ matrix.target }} cross build --all-features --release --target ${{ matrix.target }}
else else
if [[ "${{ matrix.target }}" == "x86_64-win7-windows-msvc" || "${{ matrix.target }}" == "i686-win7-windows-msvc" ]]; then if [[ "${{ matrix.target }}" == "x86_64-win7-windows-msvc" || "${{ matrix.target }}" == "i686-win7-windows-msvc" ]]; then
@ -105,7 +105,7 @@ jobs:
./build-apple.sh ./build-apple.sh
zip -r mypubdir4/tun2proxy-apple-xcframework.zip ./tun2proxy.xcframework/ zip -r mypubdir4/tun2proxy-apple-xcframework.zip ./tun2proxy.xcframework/
fi fi
elif [[ "${{ contains(matrix.host_os, 'ubuntu') }}" == "true" ]]; then elif [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
zip -j mypubdir4/tun2proxy-${{ matrix.target }}.zip target/${{ matrix.target }}/release/tun2proxy-bin target/${{ matrix.target }}/release/udpgw-server README.md target/tun2proxy.h target/${{ matrix.target }}/release/libtun2proxy.so zip -j mypubdir4/tun2proxy-${{ matrix.target }}.zip target/${{ matrix.target }}/release/tun2proxy-bin target/${{ matrix.target }}/release/udpgw-server README.md target/tun2proxy.h target/${{ matrix.target }}/release/libtun2proxy.so
if [[ "${{ matrix.target }}" == "x86_64-unknown-linux-gnu" ]]; then if [[ "${{ matrix.target }}" == "x86_64-unknown-linux-gnu" ]]; then
./build-android.sh ./build-android.sh

View file

@ -77,7 +77,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install cargo lipo and rust compiler for ios target - name: Install cargo lipo and rust compiler for ios target
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
run: | run: |

View file

@ -1,14 +1,14 @@
[package] [package]
name = "tun2proxy" name = "tun2proxy"
version = "0.7.8" version = "0.7.2"
edition = "2024" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/tun2proxy/tun2proxy" repository = "https://github.com/tun2proxy/tun2proxy"
homepage = "https://github.com/tun2proxy/tun2proxy" homepage = "https://github.com/tun2proxy/tun2proxy"
authors = ["B. Blechschmidt", "ssrlive"] authors = ["B. Blechschmidt", "ssrlive"]
description = "Tunnel interface to proxy" description = "Tunnel interface to proxy"
readme = "README.md" readme = "README.md"
rust-version = "1.85" rust-version = "1.80"
[lib] [lib]
crate-type = ["staticlib", "cdylib", "lib"] crate-type = ["staticlib", "cdylib", "lib"]
@ -36,13 +36,12 @@ digest_auth = "0.3"
dotenvy = "0.15" dotenvy = "0.15"
env_logger = "0.11" env_logger = "0.11"
hashlink = "0.10" hashlink = "0.10"
hickory-proto = "0.25" hickory-proto = "0.24"
httparse = "1" httparse = "1"
ipstack = { version = "0.3", git = "https://github.com/ssrlive/ipstack.git", rev = "53c648e" } ipstack = { version = "0.1" }
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
mimalloc = { version = "0.1", default-features = false, optional = true } mimalloc = { version = "0.1", default-features = false, optional = true }
percent-encoding = "2" percent-encoding = "2"
shlex = "1.3.0"
socks5-impl = { version = "0.6", default-features = false, features = [ socks5-impl = { version = "0.6", default-features = false, features = [
"tokio", "tokio",
] } ] }
@ -56,15 +55,14 @@ unicase = "2"
url = "2" url = "2"
[build-dependencies] [build-dependencies]
chrono = "0.4"
serde_json = "1" serde_json = "1"
[target.'cfg(target_os="linux")'.dependencies] [target.'cfg(target_os="linux")'.dependencies]
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
bincode = "2" bincode = "1"
[target.'cfg(target_os="android")'.dependencies] [target.'cfg(target_os="android")'.dependencies]
android_logger = "0.15" android_logger = "0.14"
jni = { version = "0.21", default-features = false } jni = { version = "0.21", default-features = false }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
@ -76,7 +74,7 @@ nix = { version = "0.29", default-features = false, features = [
] } ] }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows-service = "0.8" windows-service = "0.7"
# [profile.release] [profile.release]
# strip = "symbols" strip = "symbols"

View file

@ -1,13 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
if let Ok(git_hash) = get_git_hash() {
// Set the environment variables
println!("cargo:rustc-env=GIT_HASH={}", git_hash.trim());
}
// Get the build time
let build_time = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
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() {
let mut f = std::fs::File::create(cargo_target_dir.join("build.log"))?; let mut f = std::fs::File::create(cargo_target_dir.join("build.log"))?;
@ -94,10 +85,3 @@ fn get_crate_dir(crate_name: &str) -> Result<std::path::PathBuf, Box<dyn std::er
} }
Ok(crate_dir.ok_or("crate_dir")?) Ok(crate_dir.ok_or("crate_dir")?)
} }
fn get_git_hash() -> std::io::Result<String> {
use std::process::Command;
let git_hash = Command::new("git").args(["rev-parse", "--short", "HEAD"]).output()?.stdout;
let git_hash = String::from_utf8(git_hash).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
Ok(git_hash)
}

View file

@ -1,14 +1,14 @@
#![cfg(target_os = "android")] #![cfg(target_os = "android")]
use crate::{ use crate::{
Args,
args::ArgProxy, args::ArgProxy,
error::{Error, Result}, error::{Error, Result},
Args,
}; };
use jni::{ use jni::{
JNIEnv,
objects::{JClass, JString}, objects::{JClass, JString},
sys::{jboolean, jchar, jint}, sys::{jboolean, jchar, jint},
JNIEnv,
}; };
/// # Safety /// # Safety
@ -21,7 +21,7 @@ use jni::{
/// - tun_mtu: the tun mtu /// - tun_mtu: the tun mtu
/// - dns_strategy: the dns strategy, see ArgDns enum /// - dns_strategy: the dns strategy, see ArgDns enum
/// - verbosity: the verbosity level, see ArgVerbosity enum /// - verbosity: the verbosity level, see ArgVerbosity enum
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run( pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
mut env: JNIEnv, mut env: JNIEnv,
_clazz: JClass, _clazz: JClass,
@ -58,7 +58,7 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
/// # Safety /// # Safety
/// ///
/// Shutdown tun2proxy /// Shutdown tun2proxy
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(_env: JNIEnv, _: JClass) -> jint { pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(_env: JNIEnv, _: JClass) -> jint {
crate::general_api::tun2proxy_stop_internal() crate::general_api::tun2proxy_stop_internal()
} }

View file

@ -9,7 +9,7 @@ use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone, clap::Parser)] #[derive(Debug, Clone, clap::Parser)]
#[command(author, version = version_info(), about = "Tunnel interface to proxy.", long_about = None)] #[command(author, version, about = "Tunnel interface to proxy.", long_about = None)]
pub struct Args { pub struct Args {
/// Proxy URL in the form proto://[username[:password]@]host:port, /// Proxy URL in the form proto://[username[:password]@]host:port,
/// where proto is one of socks4, socks5, http. /// where proto is one of socks4, socks5, http.
@ -127,10 +127,6 @@ pub struct Args {
pub udpgw_keepalive: Option<u64>, pub udpgw_keepalive: Option<u64>,
} }
fn version_info() -> &'static str {
concat!(env!("CARGO_PKG_VERSION"), " (", env!("GIT_HASH"), " ", env!("BUILD_TIME"), ")")
}
fn validate_tun(p: &str) -> Result<String> { fn validate_tun(p: &str) -> Result<String> {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
if p.len() <= 4 || &p[..4] != "utun" { if p.len() <= 4 || &p[..4] != "utun" {

View file

@ -1,4 +1,5 @@
use tun2proxy::{ArgVerbosity, Args, BoxError}; use tun::DEFAULT_MTU as MTU;
use tun2proxy::{Args, BoxError};
fn main() -> Result<(), BoxError> { fn main() -> Result<(), BoxError> {
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
@ -28,16 +29,11 @@ fn main() -> Result<(), BoxError> {
} }
async fn main_async(args: Args) -> Result<(), BoxError> { async fn main_async(args: Args) -> Result<(), BoxError> {
let ipstack = match args.verbosity { let default = format!("{:?},hickory_proto=warn", args.verbosity);
ArgVerbosity::Trace => ArgVerbosity::Debug,
_ => args.verbosity,
};
let default = format!("{:?},hickory_proto=warn,ipstack={:?}", args.verbosity, ipstack);
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();
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({
let args = args.clone();
let shutdown_token = shutdown_token.clone(); let shutdown_token = shutdown_token.clone();
async move { async move {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -45,20 +41,18 @@ async fn main_async(args: Args) -> Result<(), BoxError> {
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;
} }
unsafe extern "C" fn traffic_cb(status: *const tun2proxy::TrafficStatus, _: *mut std::ffi::c_void) { unsafe extern "C" fn traffic_cb(status: *const tun2proxy::TrafficStatus, _: *mut std::ffi::c_void) {
let status = unsafe { &*status }; let status = &*status;
log::debug!("Traffic: ▲ {} : ▼ {}", status.tx, status.rx); log::debug!("Traffic: ▲ {} : ▼ {}", status.tx, status.rx);
} }
unsafe { tun2proxy::tun2proxy_set_traffic_status_callback(1, Some(traffic_cb), std::ptr::null_mut()) }; unsafe { tun2proxy::tun2proxy_set_traffic_status_callback(1, Some(traffic_cb), std::ptr::null_mut()) };
let ret = tun2proxy::general_run_async(args, tun::DEFAULT_MTU, cfg!(target_os = "macos"), shutdown_token).await; if let Err(err) = tun2proxy::general_run_async(args, MTU, false, shutdown_token).await {
if let Err(err) = &ret { log::error!("main loop error: {}", err);
log::error!("main loop error: {err}");
} }
ret
} }
}); });
@ -71,19 +65,13 @@ async fn main_async(args: Args) -> Result<(), BoxError> {
}) })
.await; .await;
let tasks = main_loop_handle.await??; 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.map_err(|err| err.to_string())?;
} }
if args.exit_on_fatal_error && tasks >= args.max_sessions {
// Because `main_async` function perhaps stuck in `await` state, so we need to exit the process forcefully
log::info!("Internal fatal error, max sessions reached ({tasks}/{})", args.max_sessions);
std::process::exit(-1);
}
Ok(()) Ok(())
} }
@ -92,7 +80,7 @@ async fn namespace_proxy_main(
_args: Args, _args: Args,
_shutdown_token: tokio_util::sync::CancellationToken, _shutdown_token: tokio_util::sync::CancellationToken,
) -> Result<std::process::ExitStatus, tun2proxy::Error> { ) -> Result<std::process::ExitStatus, tun2proxy::Error> {
use nix::fcntl::{OFlag, open}; use nix::fcntl::{open, OFlag};
use nix::sys::stat::Mode; use nix::sys::stat::Mode;
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;

View file

@ -3,14 +3,14 @@ use std::net::SocketAddr;
use tokio::{ use tokio::{
io::AsyncWriteExt, io::AsyncWriteExt,
net::{ net::{
UdpSocket,
tcp::{ReadHalf, WriteHalf}, tcp::{ReadHalf, WriteHalf},
UdpSocket,
}, },
sync::mpsc::{Receiver, Sender}, sync::mpsc::{Receiver, Sender},
}; };
use tun2proxy::{ use tun2proxy::{
ArgVerbosity, BoxError, Error, Result,
udpgw::{Packet, UdpFlag}, udpgw::{Packet, UdpFlag},
ArgVerbosity, BoxError, Error, Result,
}; };
pub(crate) const CLIENT_DISCONNECT_TIMEOUT: tokio::time::Duration = std::time::Duration::from_secs(60); pub(crate) const CLIENT_DISCONNECT_TIMEOUT: tokio::time::Duration = std::time::Duration::from_secs(60);

View file

@ -1,16 +1,21 @@
use hickory_proto::{ use hickory_proto::{
op::{Message, MessageType, ResponseCode}, op::{Message, MessageType, ResponseCode},
rr::{ rr::{record_type::RecordType, Name, RData, Record},
Name, RData, Record,
rdata::{A, AAAA},
},
}; };
use std::{net::IpAddr, str::FromStr}; use std::{net::IpAddr, str::FromStr};
pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u32) -> Result<Message, String> { pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u32) -> Result<Message, String> {
let record = match ip { let record = match ip {
IpAddr::V4(ip) => Record::from_rdata(Name::from_str(domain)?, ttl, RData::A(A(ip))), IpAddr::V4(ip) => {
IpAddr::V6(ip) => Record::from_rdata(Name::from_str(domain)?, ttl, RData::AAAA(AAAA(ip))), let mut record = Record::with(Name::from_str(domain)?, RecordType::A, ttl);
record.set_data(Some(RData::A(ip.into())));
record
}
IpAddr::V6(ip) => {
let mut record = Record::with(Name::from_str(domain)?, RecordType::AAAA, ttl);
record.set_data(Some(RData::AAAA(ip.into())));
record
}
}; };
// We must indicate that this message is a response. Otherwise, implementations may not // We must indicate that this message is a response. Otherwise, implementations may not
@ -22,7 +27,9 @@ pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u
} }
pub fn remove_ipv6_entries(message: &mut Message) { pub fn remove_ipv6_entries(message: &mut Message) {
message.answers_mut().retain(|answer| !matches!(answer.data(), RData::AAAA(_))); message
.answers_mut()
.retain(|answer| !matches!(answer.data(), Some(RData::AAAA(_))));
} }
pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result<IpAddr, String> { pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result<IpAddr, String> {
@ -31,7 +38,7 @@ pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result<IpAddr, Stri
} }
let mut cname = None; let mut cname = None;
for answer in message.answers() { for answer in message.answers() {
match answer.data() { match answer.data().ok_or("DNS response not contains answer data")? {
RData::A(addr) => { RData::A(addr) => {
return Ok(IpAddr::V4((*addr).into())); return Ok(IpAddr::V4((*addr).into()));
} }

View file

@ -9,7 +9,7 @@ pub(crate) static DUMP_CALLBACK: Mutex<Option<DumpCallback>> = Mutex::new(None);
/// # Safety /// # Safety
/// ///
/// set dump log info callback. /// set dump log info callback.
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_set_log_callback( pub unsafe extern "C" fn tun2proxy_set_log_callback(
callback: Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>, callback: Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>,
ctx: *mut c_void, ctx: *mut c_void,
@ -23,7 +23,7 @@ pub struct DumpCallback(Option<unsafe extern "C" fn(ArgVerbosity, *const c_char,
impl DumpCallback { impl DumpCallback {
unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) { unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) {
if let Some(cb) = self.0 { if let Some(cb) = self.0 {
unsafe { cb(dump_level, info, self.1) }; cb(dump_level, info, self.1);
} }
} }
} }

View file

@ -26,7 +26,7 @@ pub enum Error {
IpStack(#[from] ipstack::IpStackError), IpStack(#[from] ipstack::IpStackError),
#[error("DnsProtoError {0:?}")] #[error("DnsProtoError {0:?}")]
DnsProto(#[from] hickory_proto::ProtoError), DnsProto(#[from] hickory_proto::error::ProtoError),
#[error("httparse::Error {0:?}")] #[error("httparse::Error {0:?}")]
Httparse(#[from] httparse::Error), Httparse(#[from] httparse::Error),
@ -43,6 +43,10 @@ pub enum Error {
#[error("std::num::ParseIntError {0:?}")] #[error("std::num::ParseIntError {0:?}")]
IntParseError(#[from] std::num::ParseIntError), IntParseError(#[from] std::num::ParseIntError),
#[cfg(target_os = "linux")]
#[error("bincode::Error {0:?}")]
BincodeError(#[from] bincode::Error),
} }
impl From<&str> for Error { impl From<&str> for Error {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
ArgVerbosity, Args,
args::{ArgDns, ArgProxy}, args::{ArgDns, ArgProxy},
ArgVerbosity, Args,
}; };
use std::os::raw::{c_char, c_int, c_ushort}; use std::os::raw::{c_char, c_int, c_ushort};
@ -16,7 +16,7 @@ static TUN_QUIT: std::sync::Mutex<Option<tokio_util::sync::CancellationToken>> =
/// - dns_strategy: the dns strategy, see ArgDns enum /// - dns_strategy: the dns strategy, see ArgDns enum
/// - root_privilege: whether to run with root privilege /// - root_privilege: whether to run with root privilege
/// - verbosity: the verbosity level, see ArgVerbosity enum /// - verbosity: the verbosity level, see ArgVerbosity enum
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_with_name_run( pub unsafe extern "C" fn tun2proxy_with_name_run(
proxy_url: *const c_char, proxy_url: *const c_char,
tun: *const c_char, tun: *const c_char,
@ -25,12 +25,12 @@ pub unsafe extern "C" fn tun2proxy_with_name_run(
_root_privilege: bool, _root_privilege: bool,
verbosity: ArgVerbosity, verbosity: ArgVerbosity,
) -> c_int { ) -> c_int {
let proxy_url = unsafe { std::ffi::CStr::from_ptr(proxy_url) }.to_str().unwrap(); let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap();
let proxy = ArgProxy::try_from(proxy_url).unwrap(); let proxy = ArgProxy::try_from(proxy_url).unwrap();
let tun = unsafe { std::ffi::CStr::from_ptr(tun) }.to_str().unwrap().to_string(); let tun = std::ffi::CStr::from_ptr(tun).to_str().unwrap().to_string();
let mut args = Args::default(); let mut args = Args::default();
if let Ok(bypass) = unsafe { std::ffi::CStr::from_ptr(bypass) }.to_str() { if let Ok(bypass) = std::ffi::CStr::from_ptr(bypass).to_str() {
args.bypass(bypass.parse().unwrap()); args.bypass(bypass.parse().unwrap());
} }
args.proxy(proxy).tun(tun).dns(dns_strategy).verbosity(verbosity); args.proxy(proxy).tun(tun).dns(dns_strategy).verbosity(verbosity);
@ -53,7 +53,7 @@ pub unsafe extern "C" fn tun2proxy_with_name_run(
/// - dns_strategy: the dns strategy, see ArgDns enum /// - dns_strategy: the dns strategy, see ArgDns enum
/// - verbosity: the verbosity level, see ArgVerbosity enum /// - verbosity: the verbosity level, see ArgVerbosity enum
#[cfg(unix)] #[cfg(unix)]
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_with_fd_run( pub unsafe extern "C" fn tun2proxy_with_fd_run(
proxy_url: *const c_char, proxy_url: *const c_char,
tun_fd: c_int, tun_fd: c_int,
@ -63,7 +63,7 @@ pub unsafe extern "C" fn tun2proxy_with_fd_run(
dns_strategy: ArgDns, dns_strategy: ArgDns,
verbosity: ArgVerbosity, verbosity: ArgVerbosity,
) -> c_int { ) -> c_int {
let proxy_url = unsafe { std::ffi::CStr::from_ptr(proxy_url) }.to_str().unwrap(); let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap();
let proxy = ArgProxy::try_from(proxy_url).unwrap(); let proxy = ArgProxy::try_from(proxy_url).unwrap();
let mut args = Args::default(); let mut args = Args::default();
@ -83,17 +83,12 @@ pub unsafe extern "C" fn tun2proxy_with_fd_run(
/// e.g. `tun2proxy-bin --setup --proxy socks5://127.0.0.1:1080 --bypass 98.76.54.0/24 --dns over-tcp --verbosity trace` /// e.g. `tun2proxy-bin --setup --proxy socks5://127.0.0.1:1080 --bypass 98.76.54.0/24 --dns over-tcp --verbosity trace`
/// - tun_mtu: The MTU of the TUN device, e.g. 1500 /// - tun_mtu: The MTU of the TUN device, e.g. 1500
/// - packet_information: Whether exists packet information in packet from TUN device /// - packet_information: Whether exists packet information in packet from TUN device
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_run_with_cli_args(cli_args: *const c_char, tun_mtu: c_ushort, packet_information: bool) -> c_int { pub unsafe extern "C" fn tun2proxy_run_with_cli_args(cli_args: *const c_char, tun_mtu: c_ushort, packet_information: bool) -> c_int {
let Ok(cli_args) = unsafe { std::ffi::CStr::from_ptr(cli_args) }.to_str() else { let Ok(cli_args) = std::ffi::CStr::from_ptr(cli_args).to_str() else {
log::error!("Failed to convert CLI arguments to string");
return -5; return -5;
}; };
let Some(args) = shlex::split(cli_args) else { let args = <Args as ::clap::Parser>::parse_from(cli_args.split_whitespace());
log::error!("Failed to split CLI arguments");
return -6;
};
let args = <Args as ::clap::Parser>::parse_from(args);
general_run_for_api(args, tun_mtu, packet_information) general_run_for_api(args, tun_mtu, packet_information)
} }
@ -120,18 +115,11 @@ pub fn general_run_for_api(args: Args, tun_mtu: u16, packet_information: bool) -
return -3; return -3;
}; };
match rt.block_on(async move { match rt.block_on(async move {
let ret = general_run_async(args.clone(), tun_mtu, packet_information, shutdown_token).await; if let Err(err) = general_run_async(args, tun_mtu, packet_information, shutdown_token).await {
match &ret { log::error!("main loop error: {}", err);
Ok(sessions) => { return Err(err);
if args.exit_on_fatal_error && *sessions >= args.max_sessions {
log::error!("Forced exit due to max sessions reached ({sessions}/{})", args.max_sessions);
std::process::exit(-1);
}
log::debug!("tun2proxy exited normally, current sessions: {sessions}");
}
Err(err) => log::error!("main loop error: {err}"),
} }
ret Ok(())
}) { }) {
Ok(_) => 0, Ok(_) => 0,
Err(e) => { Err(e) => {
@ -147,7 +135,7 @@ pub async fn general_run_async(
tun_mtu: u16, tun_mtu: u16,
_packet_information: bool, _packet_information: bool,
shutdown_token: tokio_util::sync::CancellationToken, shutdown_token: tokio_util::sync::CancellationToken,
) -> std::io::Result<usize> { ) -> std::io::Result<()> {
let mut tun_config = tun::Configuration::default(); let mut tun_config = tun::Configuration::default();
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
@ -253,7 +241,7 @@ pub async fn general_run_async(
/// # Safety /// # Safety
/// ///
/// Shutdown the tun2proxy component. /// Shutdown the tun2proxy component.
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_stop() -> c_int { pub unsafe extern "C" fn tun2proxy_stop() -> c_int {
tun2proxy_stop_internal() tun2proxy_stop_internal()
} }

View file

@ -7,7 +7,7 @@ use crate::{
use httparse::Response; use httparse::Response;
use socks5_impl::protocol::UserKey; use socks5_impl::protocol::UserKey;
use std::{ use std::{
collections::{HashMap, VecDeque, hash_map::RandomState}, collections::{hash_map::RandomState, HashMap, VecDeque},
iter::FromIterator, iter::FromIterator,
net::SocketAddr, net::SocketAddr,
str, str,

View file

@ -22,18 +22,18 @@ use std::{
use tokio::{ use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::{TcpSocket, TcpStream, UdpSocket}, net::{TcpSocket, TcpStream, UdpSocket},
sync::{Mutex, mpsc::Receiver}, sync::{mpsc::Receiver, Mutex},
}; };
pub use tokio_util::sync::CancellationToken; pub use tokio_util::sync::CancellationToken;
use tproxy_config::is_private_ip; use tproxy_config::is_private_ip;
use udp_stream::UdpStream; use udp_stream::UdpStream;
#[cfg(feature = "udpgw")] #[cfg(feature = "udpgw")]
use udpgw::{UDPGW_KEEPALIVE_TIME, UDPGW_MAX_CONNECTIONS, UdpGwClientStream, UdpGwResponse}; use udpgw::{UdpGwClientStream, UdpGwResponse, UDPGW_KEEPALIVE_TIME, UDPGW_MAX_CONNECTIONS};
pub use { pub use {
args::{ArgDns, ArgProxy, ArgVerbosity, Args, ProxyType}, args::{ArgDns, ArgProxy, ArgVerbosity, Args, ProxyType},
error::{BoxError, Error, Result}, error::{BoxError, Error, Result},
traffic_status::{TrafficStatus, tun2proxy_set_traffic_status_callback}, traffic_status::{tun2proxy_set_traffic_status_callback, TrafficStatus},
}; };
#[cfg(feature = "mimalloc")] #[cfg(feature = "mimalloc")]
@ -64,12 +64,12 @@ pub mod win_svc;
const DNS_PORT: u16 = 53; const DNS_PORT: u16 = 53;
static TASK_COUNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
use std::sync::atomic::Ordering::Relaxed;
#[allow(unused)] #[allow(unused)]
#[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)] #[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr( #[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
target_os = "linux",
derive(bincode::Encode, bincode::Decode, serde::Serialize, serde::Deserialize)
)]
pub enum SocketProtocol { pub enum SocketProtocol {
Tcp, Tcp,
Udp, Udp,
@ -77,10 +77,7 @@ pub enum SocketProtocol {
#[allow(unused)] #[allow(unused)]
#[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)] #[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr( #[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
target_os = "linux",
derive(bincode::Encode, bincode::Decode, serde::Serialize, serde::Deserialize)
)]
pub enum SocketDomain { pub enum SocketDomain {
IpV4, IpV4,
IpV6, IpV6,
@ -151,9 +148,7 @@ async fn create_udp_stream(socket_queue: &Option<Arc<SocketQueue>>, peer: Socket
/// * `mtu` - The MTU of the network device /// * `mtu` - The MTU of the network device
/// * `args` - The arguments to use /// * `args` - The arguments to use
/// * `shutdown_token` - The token to exit the server /// * `shutdown_token` - The token to exit the server
/// # Returns pub async fn run<D>(device: D, mtu: u16, args: Args, shutdown_token: CancellationToken) -> crate::Result<()>
/// * The number of sessions while exiting
pub async fn run<D>(device: D, mtu: u16, args: Args, shutdown_token: CancellationToken) -> crate::Result<usize>
where where
D: AsyncRead + AsyncWrite + Unpin + Send + 'static, D: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{ {
@ -221,11 +216,11 @@ where
let socket_queue = None; let socket_queue = None;
use socks5_impl::protocol::Version::{V4, V5}; use socks5_impl::protocol::Version::{V4, V5};
let mgr: Arc<dyn ProxyHandlerManager> = match args.proxy.proxy_type { let mgr = match args.proxy.proxy_type {
ProxyType::Socks5 => Arc::new(SocksProxyManager::new(server_addr, V5, key)), ProxyType::Socks5 => Arc::new(SocksProxyManager::new(server_addr, V5, key)) as Arc<dyn ProxyHandlerManager>,
ProxyType::Socks4 => Arc::new(SocksProxyManager::new(server_addr, V4, key)), ProxyType::Socks4 => Arc::new(SocksProxyManager::new(server_addr, V4, key)) as Arc<dyn ProxyHandlerManager>,
ProxyType::Http => Arc::new(HttpManager::new(server_addr, key)), ProxyType::Http => Arc::new(HttpManager::new(server_addr, key)) as Arc<dyn ProxyHandlerManager>,
ProxyType::None => Arc::new(NoProxyManager::new()), ProxyType::None => Arc::new(NoProxyManager::new()) as Arc<dyn ProxyHandlerManager>,
}; };
let mut ipstack_config = ipstack::IpStackConfig::default(); let mut ipstack_config = ipstack::IpStackConfig::default();
@ -253,11 +248,7 @@ where
client client
}); });
let task_count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
use std::sync::atomic::Ordering::Relaxed;
loop { loop {
let task_count = task_count.clone();
let virtual_dns = virtual_dns.clone(); let virtual_dns = virtual_dns.clone();
let ip_stack_stream = tokio::select! { let ip_stack_stream = tokio::select! {
_ = shutdown_token.cancelled() => { _ = shutdown_token.cancelled() => {
@ -268,10 +259,10 @@ where
ip_stack_stream? ip_stack_stream?
} }
}; };
let max_sessions = args.max_sessions; let max_sessions = args.max_sessions as u64;
match ip_stack_stream { match ip_stack_stream {
IpStackStream::Tcp(tcp) => { IpStackStream::Tcp(tcp) => {
if task_count.load(Relaxed) >= max_sessions { if TASK_COUNT.load(Relaxed) > max_sessions {
if args.exit_on_fatal_error { if args.exit_on_fatal_error {
log::info!("Too many sessions that over {max_sessions}, exiting..."); log::info!("Too many sessions that over {max_sessions}, exiting...");
break; break;
@ -279,7 +270,7 @@ where
log::warn!("Too many sessions that over {max_sessions}, dropping new session"); log::warn!("Too many sessions that over {max_sessions}, dropping new session");
continue; continue;
} }
log::trace!("Session count {}", task_count.fetch_add(1, Relaxed).saturating_add(1)); log::trace!("Session count {}", TASK_COUNT.fetch_add(1, Relaxed) + 1);
let info = SessionInfo::new(tcp.local_addr(), tcp.peer_addr(), IpProtocol::Tcp); let info = SessionInfo::new(tcp.local_addr(), tcp.peer_addr(), IpProtocol::Tcp);
let domain_name = if let Some(virtual_dns) = &virtual_dns { let domain_name = if let Some(virtual_dns) = &virtual_dns {
let mut virtual_dns = virtual_dns.lock().await; let mut virtual_dns = virtual_dns.lock().await;
@ -294,11 +285,11 @@ where
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!("{} error \"{}\"", info, err);
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", TASK_COUNT.fetch_sub(1, Relaxed) - 1);
}); });
} }
IpStackStream::Udp(udp) => { IpStackStream::Udp(udp) => {
if task_count.load(Relaxed) >= max_sessions { if TASK_COUNT.load(Relaxed) > max_sessions {
if args.exit_on_fatal_error { if args.exit_on_fatal_error {
log::info!("Too many sessions that over {max_sessions}, exiting..."); log::info!("Too many sessions that over {max_sessions}, exiting...");
break; break;
@ -306,11 +297,11 @@ where
log::warn!("Too many sessions that over {max_sessions}, dropping new session"); log::warn!("Too many sessions that over {max_sessions}, dropping new session");
continue; continue;
} }
log::trace!("Session count {}", task_count.fetch_add(1, Relaxed).saturating_add(1)); log::trace!("Session count {}", TASK_COUNT.fetch_add(1, Relaxed) + 1);
let mut info = SessionInfo::new(udp.local_addr(), udp.peer_addr(), IpProtocol::Udp); let mut info = SessionInfo::new(udp.local_addr(), udp.peer_addr(), IpProtocol::Udp);
if info.dst.port() == DNS_PORT { if info.dst.port() == DNS_PORT {
if is_private_ip(info.dst.ip()) { if is_private_ip(info.dst.ip()) {
info.dst.set_ip(dns_addr); // !!! Here we change the destination address to remote DNS server!!! info.dst.set_ip(dns_addr);
} }
if args.dns == ArgDns::OverTcp { if args.dns == ArgDns::OverTcp {
info.protocol = IpProtocol::Tcp; info.protocol = IpProtocol::Tcp;
@ -320,7 +311,7 @@ where
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!("{} error \"{}\"", info, err);
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", TASK_COUNT.fetch_sub(1, Relaxed) - 1);
}); });
continue; continue;
} }
@ -331,7 +322,7 @@ where
log::error!("{} error \"{}\"", info, err); log::error!("{} error \"{}\"", info, err);
} }
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", TASK_COUNT.fetch_sub(1, Relaxed) - 1);
}); });
continue; continue;
} }
@ -362,7 +353,7 @@ where
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 {} with \"{}\"", info, e);
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", TASK_COUNT.fetch_sub(1, Relaxed) - 1);
}); });
continue; continue;
} }
@ -374,7 +365,7 @@ where
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 {} with \"{}\"", info, err);
} }
log::trace!("Session count {}", task_count.fetch_sub(1, Relaxed).saturating_sub(1)); log::trace!("Session count {}", TASK_COUNT.fetch_sub(1, Relaxed) - 1);
}); });
} }
Err(e) => { Err(e) => {
@ -384,7 +375,7 @@ where
} }
IpStackStream::UnknownTransport(u) => { IpStackStream::UnknownTransport(u) => {
let len = u.payload().len(); let len = u.payload().len();
log::info!("#0 unhandled transport - Ip Protocol {:?}, length {}", u.ip_protocol(), len); log::info!("#0 unhandled transport - Ip Protocol 0x{:02X}, length {}", u.ip_protocol(), len);
continue; continue;
} }
IpStackStream::UnknownNetwork(pkt) => { IpStackStream::UnknownNetwork(pkt) => {
@ -393,7 +384,7 @@ where
} }
} }
} }
Ok(task_count.load(Relaxed)) Ok(())
} }
async fn handle_virtual_dns_session(mut udp: IpStackUdpStream, dns: Arc<Mutex<VirtualDns>>) -> crate::Result<()> { async fn handle_virtual_dns_session(mut udp: IpStackUdpStream, dns: Arc<Mutex<VirtualDns>>) -> crate::Result<()> {

View file

@ -1,10 +1,10 @@
#![cfg(target_os = "linux")] #![cfg(target_os = "linux")]
use crate::{SocketDomain, SocketProtocol, error}; use crate::{error, SocketDomain, SocketProtocol};
use nix::{ use nix::{
errno::Errno, errno::Errno,
fcntl::{self, FdFlag}, fcntl::{self, FdFlag},
sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags, SockType, cmsg_space, getsockopt, recvmsg, sendmsg, sockopt}, sys::socket::{cmsg_space, getsockopt, recvmsg, sendmsg, sockopt, ControlMessage, ControlMessageOwned, MsgFlags, SockType},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -16,14 +16,14 @@ use tokio::net::{TcpSocket, UdpSocket, UnixDatagram};
const REQUEST_BUFFER_SIZE: usize = 64; const REQUEST_BUFFER_SIZE: usize = 64;
#[derive(bincode::Encode, bincode::Decode, Hash, Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] #[derive(Hash, Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
struct Request { struct Request {
protocol: SocketProtocol, protocol: SocketProtocol,
domain: SocketDomain, domain: SocketDomain,
number: u32, number: u32,
} }
#[derive(bincode::Encode, bincode::Decode, PartialEq, Debug, Hash, Copy, Clone, Eq, Serialize, Deserialize)] #[derive(Hash, Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
enum Response { enum Response {
Ok, Ok,
} }
@ -135,21 +135,14 @@ where
// Borrow socket as mut to prevent multiple simultaneous requests // Borrow socket as mut to prevent multiple simultaneous requests
let socket = socket.deref_mut(); let socket = socket.deref_mut();
let mut request = [0u8; 1000];
// Send request // Send request
let size = bincode::encode_into_slice( let request = bincode::serialize(&Request {
Request { protocol: T::domain(),
protocol: T::domain(), domain,
domain, number,
number, })?;
},
&mut request,
bincode::config::standard(),
)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
socket.send(&request[..size]).await?; socket.send(&request[..]).await?;
// Receive response // Receive response
loop { loop {
@ -168,9 +161,7 @@ where
// Parse response // Parse response
let response = &msg.iovs().next().unwrap()[..msg.bytes]; let response = &msg.iovs().next().unwrap()[..msg.bytes];
let response: Response = bincode::decode_from_slice(response, bincode::config::standard()) let response: Response = bincode::deserialize(response)?;
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?
.0;
if !matches!(response, Response::Ok) { if !matches!(response, Response::Ok) {
return Err("Request for new sockets failed".into()); return Err("Request for new sockets failed".into());
} }
@ -203,14 +194,10 @@ pub async fn process_socket_requests(socket: &UnixDatagram) -> error::Result<()>
let len = socket.recv(&mut buf[..]).await?; let len = socket.recv(&mut buf[..]).await?;
let request: Request = bincode::decode_from_slice(&buf[..len], bincode::config::standard()) let request: Request = bincode::deserialize(&buf[..len])?;
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?
.0;
let response = Response::Ok; let response = Response::Ok;
let mut buf = [0u8; 1000]; let buf = bincode::serialize(&response)?;
let size = bincode::encode_into_slice(response, &mut buf, bincode::config::standard())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let mut owned_fd_buf: Vec<OwnedFd> = Vec::with_capacity(request.number as usize); let mut owned_fd_buf: Vec<OwnedFd> = Vec::with_capacity(request.number as usize);
for _ in 0..request.number { for _ in 0..request.number {
@ -236,7 +223,7 @@ pub async fn process_socket_requests(socket: &UnixDatagram) -> error::Result<()>
let raw_fd_buf: Vec<RawFd> = owned_fd_buf.iter().map(|fd| fd.as_raw_fd()).collect(); let raw_fd_buf: Vec<RawFd> = owned_fd_buf.iter().map(|fd| fd.as_raw_fd()).collect();
let cmsg = ControlMessage::ScmRights(&raw_fd_buf[..]); let cmsg = ControlMessage::ScmRights(&raw_fd_buf[..]);
let iov = [IoSlice::new(&buf[..size])]; let iov = [IoSlice::new(&buf[..])];
sendmsg::<()>(socket.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None)?; sendmsg::<()>(socket.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None)?;
} }

View file

@ -4,7 +4,7 @@ use crate::{
proxy_handler::{ProxyHandler, ProxyHandlerManager}, proxy_handler::{ProxyHandler, ProxyHandlerManager},
session_info::SessionInfo, session_info::SessionInfo,
}; };
use socks5_impl::protocol::{self, Address, AuthMethod, StreamOperation, UserKey, Version, handshake, password_method}; use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version};
use std::{collections::VecDeque, net::SocketAddr, sync::Arc}; use std::{collections::VecDeque, net::SocketAddr, sync::Arc};
use tokio::sync::Mutex; use tokio::sync::Mutex;

View file

@ -5,7 +5,7 @@ use std::sync::{LazyLock, Mutex};
/// # Safety /// # Safety
/// ///
/// set traffic status callback. /// set traffic status callback.
#[unsafe(no_mangle)] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_set_traffic_status_callback( pub unsafe extern "C" fn tun2proxy_set_traffic_status_callback(
send_interval_secs: u32, send_interval_secs: u32,
callback: Option<unsafe extern "C" fn(*const TrafficStatus, *mut c_void)>, callback: Option<unsafe extern "C" fn(*const TrafficStatus, *mut c_void)>,
@ -34,7 +34,7 @@ struct TrafficStatusCallback(Option<unsafe extern "C" fn(*const TrafficStatus, *
impl TrafficStatusCallback { impl TrafficStatusCallback {
unsafe fn call(self, info: &TrafficStatus) { unsafe fn call(self, info: &TrafficStatus) {
if let Some(cb) = self.0 { if let Some(cb) = self.0 {
unsafe { cb(info, self.1) }; cb(info, self.1);
} }
} }
} }

View file

@ -4,11 +4,11 @@ use std::{collections::VecDeque, hash::Hash, net::SocketAddr, sync::atomic::Orde
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{ net::{
TcpStream,
tcp::{OwnedReadHalf, OwnedWriteHalf}, tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
}, },
sync::Mutex, sync::Mutex,
time::{Duration, sleep}, time::{sleep, Duration},
}; };
pub(crate) const UDPGW_LENGTH_FIELD_SIZE: usize = std::mem::size_of::<u16>(); pub(crate) const UDPGW_LENGTH_FIELD_SIZE: usize = std::mem::size_of::<u16>();

View file

@ -1,5 +1,5 @@
use crate::error::Result; use crate::error::Result;
use hashlink::{LruCache, linked_hash_map::RawEntryMut}; use hashlink::{linked_hash_map::RawEntryMut, LruCache};
use std::{ use std::{
collections::HashMap, collections::HashMap,
convert::TryInto, convert::TryInto,

View file

@ -73,21 +73,13 @@ fn run_service(_arguments: Vec<std::ffi::OsString>) -> Result<(), crate::BoxErro
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?; let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(async { rt.block_on(async {
unsafe extern "C" fn traffic_cb(status: *const crate::TrafficStatus, _: *mut std::ffi::c_void) { unsafe extern "C" fn traffic_cb(status: *const crate::TrafficStatus, _: *mut std::ffi::c_void) {
let status = unsafe { &*status }; let status = &*status;
log::debug!("Traffic: ▲ {} : ▼ {}", status.tx, status.rx); log::debug!("Traffic: ▲ {} : ▼ {}", status.tx, status.rx);
} }
unsafe { crate::tun2proxy_set_traffic_status_callback(1, Some(traffic_cb), std::ptr::null_mut()) }; unsafe { crate::tun2proxy_set_traffic_status_callback(1, Some(traffic_cb), std::ptr::null_mut()) };
let ret = crate::general_run_async(args.clone(), tun::DEFAULT_MTU, false, shutdown_token).await; if let Err(err) = crate::general_run_async(args, tun::DEFAULT_MTU, false, shutdown_token).await {
match &ret { log::error!("main loop error: {}", err);
Ok(sessions) => {
if args.exit_on_fatal_error && *sessions >= args.max_sessions {
log::error!("Forced exit due to max sessions reached ({sessions}/{})", args.max_sessions);
std::process::exit(-1);
}
log::debug!("tun2proxy exited normally, current sessions: {sessions}");
}
Err(err) => log::error!("main loop error: {err}"),
} }
Ok::<(), crate::Error>(()) Ok::<(), crate::Error>(())
})?; })?;