Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: transparent proxy support (linux only) #343

Closed
wants to merge 12 commits into from
104 changes: 96 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions clash/tests/data/config/tproxy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
tproxy-port: 7893
external-controller: 127.0.0.1:9090
mode: global
log-level: debug
2 changes: 2 additions & 0 deletions clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ base64 = "0.22"
uuid = { version = "1.8.0", features = ["v4", "fast-rng", "macro-diagnostics", "serde"] }
boring = "4.5.0"
boring-sys = "4.5.0"
unix-udp-sock = { git = "https://github.com/Watfaq/unix-udp-sock.git", rev = "cd3e4eca43e6f3be82a2703c3d711b7e18fbfd18"}
cmd_lib = "1.9.3"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a big fan of executing system commands in a program - at lease should we print out what we are executing so the users know what's happening under the hood?


ip_network_table-deps-treebitmap = "0.5.0"
once_cell = "1.18.0"
Expand Down
1 change: 1 addition & 0 deletions clash_lib/src/app/dispatcher/dispatcher_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ impl Dispatcher {

/// Dispatch a UDP packet to outbound handler
/// returns the close sender
/// will ignore the source and destination in `Session`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this comment still valid?

#[instrument]
pub fn dispatch_datagram(
&self,
Expand Down
19 changes: 18 additions & 1 deletion clash_lib/src/app/inbound/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl InboundManager {
};
self.network_listeners
.values()
.for_each(|x| match x.listener_type {
.for_each(|x: &NetworkInboundListener| match x.listener_type {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary

ListenerType::Http => {
ports.port = Some(x.port);
}
Expand All @@ -98,6 +98,9 @@ impl InboundManager {
ListenerType::Mixed => {
ports.mixed_port = Some(x.port);
}
ListenerType::TProxy => {
ports.tproxy_port = Some(x.port);
}
});

ports
Expand Down Expand Up @@ -147,6 +150,20 @@ impl InboundManager {
);
}

if let Some(tproxy_port) = ports.tproxy_port {
network_listeners.insert(
ListenerType::TProxy,
NetworkInboundListener {
name: "TProxy".to_string(),
bind_addr: self.bind_address.clone(),
port: tproxy_port,
listener_type: ListenerType::TProxy,
dispatcher: self.dispatcher.clone(),
authenticator: self.authenticator.clone(),
},
);
}

self.network_listeners = network_listeners;
}
}
14 changes: 13 additions & 1 deletion clash_lib/src/app/inbound/network_listener.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::common::auth::ThreadSafeAuthenticator;
use crate::config::internal::config::BindAddress;

use crate::proxy::{http, mixed, socks, AnyInboundListener};
use crate::proxy::{http, mixed, socks, tproxy, AnyInboundListener};

use crate::proxy::utils::Interface;
use crate::{Dispatcher, Error, Runner};
Expand All @@ -17,6 +17,7 @@ pub enum ListenerType {
Http,
Socks5,
Mixed,
TProxy,
}

pub struct NetworkInboundListener {
Expand Down Expand Up @@ -111,6 +112,17 @@ impl NetworkInboundListener {
self.dispatcher.clone(),
self.authenticator.clone(),
),

ListenerType::TProxy => {
#[cfg(any(target_os = "linux", target_os = "android"))]
{
tproxy::TProxyListener::new((ip, self.port).into(), self.dispatcher.clone())
}
#[cfg(not(target_os = "linux"))]
{
warn!("tproxy only support linux and android, ignore this config");
}
}
};

if listener.handle_tcp() {
Expand Down
1 change: 1 addition & 0 deletions clash_lib/src/common/cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions clash_lib/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod auth;
pub mod cmd;
pub mod crypto;
pub mod errors;
pub mod http;
Expand All @@ -7,4 +8,5 @@ pub mod mmdb;
pub mod timed_future;
pub mod tls;
pub mod trie;
pub mod tunnel_datagram;
pub mod utils;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{net::SocketAddr, task::Poll};
use std::task::Poll;

use futures::{ready, Sink, Stream};

Expand All @@ -8,38 +8,33 @@ use crate::{
};

#[derive(Debug)]
pub struct TunDatagram {
pub struct TunnelDatagram {
// send to tproxy/tun, used for `Stream`
rx: tokio::sync::mpsc::Receiver<UdpPacket>,
// receive from tproxy/tun, used for `Sink`
tx: tokio::sync::mpsc::Sender<UdpPacket>,

pkt: Option<UdpPacket>,
flushed: bool,
#[allow(unused)]
local_addr: SocketAddr,
}

impl TunDatagram {
impl TunnelDatagram {
pub fn new(
// send to tun
tx: tokio::sync::mpsc::Sender<UdpPacket>,
// receive from tun
rx: tokio::sync::mpsc::Receiver<UdpPacket>,
// the address of the tun udp socket
local_addr: SocketAddr,
) -> Self {
Self {
rx,
tx,
pkt: None,
flushed: true,
local_addr,
}
}
}

impl InboundDatagram<UdpPacket> for TunDatagram {}
impl InboundDatagram<UdpPacket> for TunnelDatagram {}

impl Stream for TunDatagram {
impl Stream for TunnelDatagram {
type Item = UdpPacket;

fn poll_next(
Expand All @@ -50,7 +45,7 @@ impl Stream for TunDatagram {
}
}

impl Sink<UdpPacket> for TunDatagram {
impl Sink<UdpPacket> for TunnelDatagram {
type Error = std::io::Error;

fn poll_ready(
Expand Down
Loading
Loading