mod packet;
mod tcp_conn;
mod tcp_stack_direct;
mod tun_server;
mod udp_handler;
mod udp_manager;
mod wake;
#[cfg(any(target_os = "android", target_os = "ios", feature = "ffi"))]
#[cfg_attr(
all(feature = "ffi", not(any(target_os = "android", target_os = "ios"))),
allow(dead_code)
)]
mod platform;
#[cfg(any(target_os = "android", target_os = "ios", feature = "ffi"))]
#[cfg_attr(
all(feature = "ffi", not(any(target_os = "android", target_os = "ios"))),
allow(unused_imports)
)]
pub use platform::{
FnSocketProtector, NoOpPlatformCallbacks, NoOpSocketProtector, PlatformCallbacks,
PlatformInterface, SocketProtector, clear_global_socket_protector,
clear_global_socket_protector_if_current, get_global_socket_protector, protect_socket,
set_global_socket_protector,
};
pub use tun_server::TunServerConfig;
use std::net::SocketAddr;
use std::os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
use std::sync::Arc;
use log::{debug, info, warn};
use tokio::sync::{mpsc, oneshot};
use tokio::task::{JoinHandle, JoinSet};
use crate::address::{Address, NetLocation};
use crate::client_proxy_selector::ClientProxySelector;
use crate::config::TunConfig;
use crate::config::selection::ConfigSelection;
use crate::resolver::Resolver;
use crate::tcp::tcp_client_handler_factory::create_tcp_client_proxy_selector;
use packet::PacketBuffer;
use tcp_stack_direct::{NewTcpConnection, PACKET_QUEUE_CAPACITY, TcpStackDirect};
use udp_manager::TunUdpManager;
pub async fn run_tun_server(
config: TunServerConfig,
proxy_selector: Arc<ClientProxySelector>,
resolver: Arc<dyn Resolver>,
mut shutdown_rx: oneshot::Receiver<()>,
) -> std::io::Result<()> {
config.resource_limits.validate()?;
info!(
"Starting TUN server (direct mode): mtu={}, tcp={}, udp={}, icmp={}",
config.mtu, config.tcp_enabled, config.udp_enabled, config.icmp_enabled
);
let fd = if let Some(fd) = config.raw_fd {
info!("Using provided raw FD: {}", fd);
if config.close_fd_on_drop {
unsafe { OwnedFd::from_raw_fd(fd) }
} else {
unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?
}
} else {
let tun_device = config.create_sync_device()?;
let fd = tun_device.into_raw_fd();
info!("Created TUN device with FD: {}", fd);
unsafe { OwnedFd::from_raw_fd(fd) }
};
let mut stack_config = config.clone();
if config.raw_fd.is_none() && cfg!(any(target_os = "macos", target_os = "ios")) {
stack_config.packet_information = true;
}
let mut tcp_stack = TcpStackDirect::with_config(fd, stack_config)?;
let udp_from_stack_rx = tcp_stack.take_udp_rx().expect("udp_rx already taken");
let (udp_to_stack_tx, udp_to_stack_rx) = mpsc::channel::<PacketBuffer>(PACKET_QUEUE_CAPACITY);
tcp_stack.set_udp_response_tx(udp_to_stack_rx);
let (tcp_conn_tx, mut tcp_conn_rx) = mpsc::unbounded_channel::<NewTcpConnection>();
tcp_stack.set_new_conn_tx(tcp_conn_tx);
let mut tasks = JoinSet::new();
if config.tcp_enabled {
let proxy_selector = proxy_selector.clone();
let resolver = resolver.clone();
tasks.spawn(async move {
info!("Starting TCP connection handler");
let mut connections = JoinSet::new();
loop {
let new_conn = tokio::select! {
conn = tcp_conn_rx.recv() => match conn {
Some(conn) => conn,
None => break,
},
_ = connections.join_next(), if !connections.is_empty() => continue,
};
let proxy_selector = proxy_selector.clone();
let resolver = resolver.clone();
connections.spawn(async move {
let remote_addr = new_conn.remote_addr;
let target = socket_addr_to_net_location(remote_addr);
debug!("Handling TCP connection to {:?}", target);
if let Err(e) =
handle_tcp_connection(new_conn.connection, target, proxy_selector, resolver)
.await
{
debug!("TCP connection to {} failed: {}", remote_addr, e);
}
});
}
debug!("TCP connection handler ended");
});
}
if config.udp_enabled {
let proxy_selector = proxy_selector.clone();
let resolver = resolver.clone();
let limits = config.resource_limits.clone();
let wake = tcp_stack.wake_handle();
tasks.spawn(async move {
handle_udp_packets(
udp_from_stack_rx,
udp_to_stack_tx,
proxy_selector,
resolver,
limits,
wake,
)
.await;
});
}
info!("TUN server started successfully");
tokio::select! {
_ = &mut shutdown_rx => {
info!("TUN server shutdown requested");
}
_ = async {
while tcp_stack.is_running() {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
} => {
warn!("Stack thread ended unexpectedly");
}
}
tasks.shutdown().await;
info!("TUN server stopped");
Ok(())
}
fn socket_addr_to_net_location(addr: SocketAddr) -> NetLocation {
let address = match addr.ip() {
std::net::IpAddr::V4(v4) => Address::Ipv4(v4),
std::net::IpAddr::V6(v6) => Address::Ipv6(v6),
};
NetLocation::new(address, addr.port())
}
async fn handle_tcp_connection(
mut connection: tcp_conn::TcpConnection,
target: NetLocation,
proxy_selector: Arc<ClientProxySelector>,
resolver: Arc<dyn Resolver>,
) -> std::io::Result<()> {
let _permit = crate::resources::try_stream().ok_or_else(crate::resources::exhausted)?;
let decision = tokio::time::timeout(
std::time::Duration::from_secs(30),
proxy_selector.judge(target.into(), &resolver),
)
.await
.map_err(|_| std::io::Error::new(std::io::ErrorKind::TimedOut, "TUN routing timed out"))??;
match decision {
crate::client_proxy_selector::ConnectDecision::Allow {
chain_group,
remote_location,
} => {
debug!(
"TCP: connecting to {} via chain",
remote_location.location()
);
match tokio::time::timeout(
std::time::Duration::from_secs(30),
chain_group.connect_tcp(remote_location.clone(), &resolver),
)
.await
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::TimedOut, "TUN TCP setup timed out")
})? {
Ok(setup_result) => {
debug!(
"TCP: connected to {}, starting bidirectional copy",
remote_location.location()
);
let mut remote = setup_result.client_stream;
let result = tokio::io::copy_bidirectional(&mut connection, &mut remote).await;
match result {
Ok((client_to_remote, remote_to_client)) => {
debug!(
"TCP connection to {} completed: {} bytes sent, {} bytes received",
remote_location.location(),
client_to_remote,
remote_to_client
);
}
Err(e) => {
debug!(
"TCP connection to {} error: {}",
remote_location.location(),
e
);
}
}
Ok(())
}
Err(e) => {
warn!("Failed to connect to {}: {}", remote_location.location(), e);
Err(e)
}
}
}
crate::client_proxy_selector::ConnectDecision::Block => {
debug!("TCP connection blocked by rules");
Ok(())
}
}
}
async fn handle_udp_packets(
from_stack_rx: mpsc::Receiver<PacketBuffer>,
to_stack_tx: mpsc::Sender<PacketBuffer>,
proxy_selector: Arc<ClientProxySelector>,
resolver: Arc<dyn Resolver>,
limits: crate::config::tun::TunResourceLimits,
wake: wake::Wake,
) {
info!("Starting UDP handler (session-based)");
let udp_handler = udp_handler::UdpHandler::new(from_stack_rx, to_stack_tx, wake);
let (reader, writer) = udp_handler.split();
let manager = TunUdpManager::new(reader, writer, proxy_selector, resolver, limits);
if let Err(e) = manager.run().await {
warn!("UDP handler error: {}", e);
}
info!("UDP handler stopped");
}
pub async fn start_tun_server(
config: TunConfig,
resolver: std::sync::Arc<dyn crate::resolver::Resolver>,
) -> std::io::Result<JoinHandle<()>> {
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let handle = tokio::spawn(async move {
let _keep_alive = shutdown_tx;
if let Err(e) = run_tun_from_config(config, shutdown_rx, true, resolver).await {
warn!("TUN server error: {}", e);
}
});
Ok(handle)
}
pub async fn run_tun_from_config(
config: TunConfig,
shutdown_rx: tokio::sync::oneshot::Receiver<()>,
close_fd_on_drop: bool,
resolver: Arc<dyn Resolver>,
) -> std::io::Result<()> {
let mut tun_server_config = TunServerConfig::new()
.mtu(config.mtu)
.tcp_enabled(config.tcp_enabled)
.udp_enabled(config.udp_enabled)
.icmp_enabled(config.icmp_enabled)
.close_fd_on_drop(close_fd_on_drop);
tun_server_config.resource_limits = config.resource_limits;
if let Some(ref name) = config.device_name {
tun_server_config = tun_server_config.tun_name(name.clone());
println!("Starting TUN server on device {}", name);
}
if let Some(fd) = config.device_fd {
tun_server_config = tun_server_config.raw_fd(fd);
#[cfg(any(target_os = "ios", target_os = "macos"))]
{
tun_server_config = tun_server_config.packet_information(true);
}
println!("Starting TUN server from device FD {}", fd);
}
if let Some(addr) = config.address {
tun_server_config = tun_server_config.address(addr);
}
if let Some(mask) = config.netmask {
tun_server_config = tun_server_config.netmask(mask);
}
if let Some(dest) = config.destination {
tun_server_config = tun_server_config.destination(dest);
}
if let Some(packet_information) = config.packet_information {
tun_server_config.packet_information = packet_information;
}
let rules = config.rules.map(ConfigSelection::unwrap_config).into_vec();
let client_proxy_selector = Arc::new(create_tcp_client_proxy_selector(rules, resolver.clone()));
run_tun_server(
tun_server_config,
client_proxy_selector,
resolver,
shutdown_rx,
)
.await
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::fd::AsRawFd;
#[tokio::test]
async fn borrowed_tun_fd_remains_open_after_stop() {
let (_peer, client) = std::os::unix::net::UnixStream::pair().unwrap();
let config = TunServerConfig::new()
.raw_fd(client.as_raw_fd())
.close_fd_on_drop(false);
let (tx, rx) = oneshot::channel();
tx.send(()).unwrap();
run_tun_server(
config,
Arc::new(ClientProxySelector::new(Vec::new())),
Arc::new(crate::resolver::NativeResolver::new()),
rx,
)
.await
.unwrap();
assert!(unsafe { libc::fcntl(client.as_raw_fd(), libc::F_GETFD) } >= 0);
}
}