From 2fb6eadbf3fd55e73bcd8d4f7a75cfadb5db4738 Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 18:45:02 +0800 Subject: [PATCH 1/7] refactor: change fwmark to global static var --- clash_lib/src/session.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/clash_lib/src/session.rs b/clash_lib/src/session.rs index be5a17399..0ada4166e 100644 --- a/clash_lib/src/session.rs +++ b/clash_lib/src/session.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::{ io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, @@ -375,8 +376,6 @@ pub struct Session { pub source: SocketAddr, /// The proxy target address of a proxy connection. pub destination: SocksAddr, - /// The packet mark SO_MARK - pub packet_mark: Option, /// The bind interface pub iface: Option, } @@ -409,7 +408,6 @@ impl Default for Session { typ: Type::Http, source: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), destination: SocksAddr::any_ipv4(), - packet_mark: None, iface: None, } } @@ -431,7 +429,6 @@ impl Debug for Session { .field("network", &self.network) .field("source", &self.source) .field("destination", &self.destination) - .field("packet_mark", &self.packet_mark) .field("iface", &self.iface) .finish() } @@ -444,7 +441,6 @@ impl Clone for Session { typ: self.typ, source: self.source, destination: self.destination.clone(), - packet_mark: self.packet_mark, iface: self.iface.as_ref().cloned(), } } @@ -461,3 +457,15 @@ fn invalid_atyp() -> io::Error { fn insuff_bytes() -> io::Error { io::Error::new(io::ErrorKind::Other, "insufficient bytes") } + + +static GLOBAL_MARK: AtomicU32 = AtomicU32::new(0); +static ENABLE_MARK: AtomicBool = AtomicBool::new(false); +/// get socket SO_MARK that outgoing socket should use +pub fn get_somark() -> Option { + if ENABLE_MARK.load(Ordering::Relaxed) { + Some(GLOBAL_MARK.load(Ordering::Relaxed)) + } else { + None + } +} \ No newline at end of file From cb0f783e6cc95c7e1bbe963034c0f23d7f83f7cd Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 18:59:38 +0800 Subject: [PATCH 2/7] refactor: cherry pick e06531663c25fa0f893ae9094dc114ab30449c69 --- clash_lib/src/config/internal/proxy.rs | 13 +++++++++ clash_lib/src/proxy/converters/shadowsocks.rs | 4 +-- clash_lib/src/proxy/converters/trojan.rs | 4 +-- clash_lib/src/proxy/converters/vmess.rs | 4 +-- clash_lib/src/proxy/converters/wireguard.rs | 2 +- clash_lib/src/proxy/mod.rs | 28 +++++++++++++++++-- clash_lib/src/proxy/shadowsocks/mod.rs | 10 ++++--- clash_lib/src/proxy/trojan/mod.rs | 11 +++++--- clash_lib/src/proxy/vmess/mod.rs | 11 ++++---- 9 files changed, 65 insertions(+), 22 deletions(-) diff --git a/clash_lib/src/config/internal/proxy.rs b/clash_lib/src/config/internal/proxy.rs index 918e48cff..351626835 100644 --- a/clash_lib/src/config/internal/proxy.rs +++ b/clash_lib/src/config/internal/proxy.rs @@ -1,5 +1,6 @@ use crate::common::utils::default_bool_true; use crate::config::utils; +use crate::proxy::CommonOption; use crate::Error; use serde::de::value::MapDeserializer; use serde::Deserialize; @@ -116,6 +117,8 @@ impl Display for OutboundProxyProtocol { #[derive(serde::Serialize, serde::Deserialize, Debug, Default)] pub struct OutboundShadowsocks { + #[serde(flatten)] + pub common_opts: CommonOption, pub name: String, pub server: String, pub port: u16, @@ -130,6 +133,8 @@ pub struct OutboundShadowsocks { #[derive(serde::Serialize, serde::Deserialize, Debug, Default)] pub struct OutboundSocks5 { + #[serde(flatten)] + pub common_opts: CommonOption, pub name: String, pub server: String, pub port: u16, @@ -142,6 +147,8 @@ pub struct OutboundSocks5 { #[derive(serde::Serialize, serde::Deserialize, Debug, Default)] pub struct WsOpt { + #[serde(flatten)] + pub common_opts: CommonOption, pub path: Option, pub headers: Option>, pub max_early_data: Option, @@ -163,6 +170,8 @@ pub struct GrpcOpt { #[derive(serde::Serialize, serde::Deserialize, Debug, Default)] #[serde(rename_all = "kebab-case")] pub struct OutboundTrojan { + #[serde(flatten)] + pub common_opts: CommonOption, pub name: String, pub server: String, pub port: u16, @@ -179,6 +188,8 @@ pub struct OutboundTrojan { #[derive(serde::Serialize, serde::Deserialize, Debug, Default)] #[serde(rename_all = "kebab-case")] pub struct OutboundVmess { + #[serde(flatten)] + pub common_opts: CommonOption, pub name: String, pub server: String, pub port: u16, @@ -200,6 +211,8 @@ pub struct OutboundVmess { #[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)] #[serde(rename_all = "kebab-case")] pub struct OutboundWireguard { + #[serde(flatten)] + pub common_opts: CommonOption, pub name: String, pub server: String, pub port: u16, diff --git a/clash_lib/src/proxy/converters/shadowsocks.rs b/clash_lib/src/proxy/converters/shadowsocks.rs index 02adb2f9a..b99573417 100644 --- a/clash_lib/src/proxy/converters/shadowsocks.rs +++ b/clash_lib/src/proxy/converters/shadowsocks.rs @@ -2,7 +2,7 @@ use crate::{ config::internal::proxy::OutboundShadowsocks, proxy::{ shadowsocks::{Handler, HandlerOptions, OBFSOption}, - AnyOutboundHandler, CommonOption, + AnyOutboundHandler }, Error, }; @@ -21,7 +21,7 @@ impl TryFrom<&OutboundShadowsocks> for AnyOutboundHandler { fn try_from(s: &OutboundShadowsocks) -> Result { let h = Handler::new(HandlerOptions { name: s.name.to_owned(), - common_opts: CommonOption::default(), + common_opts: s.common_opts.clone(), server: s.server.to_owned(), port: s.port, password: s.password.to_owned(), diff --git a/clash_lib/src/proxy/converters/trojan.rs b/clash_lib/src/proxy/converters/trojan.rs index 212ebdf19..642f76550 100644 --- a/clash_lib/src/proxy/converters/trojan.rs +++ b/clash_lib/src/proxy/converters/trojan.rs @@ -5,7 +5,7 @@ use crate::{ proxy::{ options::{GrpcOption, WsOption}, trojan::{Handler, Opts, Transport}, - AnyOutboundHandler, CommonOption, + AnyOutboundHandler, }, Error, }; @@ -29,7 +29,7 @@ impl TryFrom<&OutboundTrojan> for AnyOutboundHandler { let h = Handler::new(Opts { name: s.name.to_owned(), - common_opts: CommonOption::default(), + common_opts: s.common_opts.clone(), server: s.server.to_owned(), port: s.port, password: s.password.clone(), diff --git a/clash_lib/src/proxy/converters/vmess.rs b/clash_lib/src/proxy/converters/vmess.rs index e04606e97..89a3731e1 100644 --- a/clash_lib/src/proxy/converters/vmess.rs +++ b/clash_lib/src/proxy/converters/vmess.rs @@ -6,7 +6,7 @@ use crate::{ options::{GrpcOption, Http2Option, WsOption}, transport::TLSOptions, vmess::{Handler, HandlerOptions, VmessTransport}, - AnyOutboundHandler, CommonOption, + AnyOutboundHandler, }, Error, }; @@ -30,7 +30,7 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { let h = Handler::new(HandlerOptions { name: s.name.to_owned(), - common_opts: CommonOption::default(), + common_opts: s.common_opts.clone(), server: s.server.to_owned(), port: s.port, uuid: s.uuid.clone(), diff --git a/clash_lib/src/proxy/converters/wireguard.rs b/clash_lib/src/proxy/converters/wireguard.rs index d1e5188c8..b482ae4f0 100644 --- a/clash_lib/src/proxy/converters/wireguard.rs +++ b/clash_lib/src/proxy/converters/wireguard.rs @@ -23,7 +23,7 @@ impl TryFrom<&OutboundWireguard> for AnyOutboundHandler { fn try_from(s: &OutboundWireguard) -> Result { let h = Handler::new(HandlerOpts { name: s.name.to_owned(), - common_opts: Default::default(), + common_opts: s.common_opts.clone(), server: s.server.to_owned(), port: s.port, ip: s diff --git a/clash_lib/src/proxy/mod.rs b/clash_lib/src/proxy/mod.rs index 96c76b826..f143ac62b 100644 --- a/clash_lib/src/proxy/mod.rs +++ b/clash_lib/src/proxy/mod.rs @@ -85,13 +85,37 @@ impl OutboundDatagram for T where pub type AnyOutboundDatagram = Box>; -#[derive(Default, Debug, Clone)] +#[allow(dead_code)] +#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CommonOption { - #[allow(dead_code)] + #[serde(rename = "interface-name")] so_mark: Option, + #[serde(rename = "routing-mark")] iface: Option, } +impl CommonOption { + pub fn merge<'a>(&'a self, sess: &'a Session) -> (Option, Option<&'a Interface>) { + let so_mark = if let Some(so_mark) = self.so_mark { + Some(so_mark) + } else if let Some(so_mark) = sess.packet_mark { + Some(so_mark) + } else { + None + }; + + let iface = if let Some(iface) = self.iface.as_ref() { + Some(iface) + } else if let Some(iface) = sess.iface.as_ref() { + Some(iface) + } else { + None + }; + + (so_mark, iface) + } +} + #[async_trait] pub trait InboundListener: Send + Sync + Unpin { /// support tcp or not diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index 86f14d083..f0fecc960 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -213,13 +213,14 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { + let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .map_err(|x| { io::Error::new( @@ -308,11 +309,12 @@ impl OutboundHandler for Handler { _ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported cipher")), }, ); + let (packet_mark, iface) = self.opts.common_opts.merge(sess); let socket = new_udp_socket( None, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .await?; let socket = ProxySocket::from_socket(UdpSocketType::Client, ctx, &cfg, socket); diff --git a/clash_lib/src/proxy/trojan/mod.rs b/clash_lib/src/proxy/trojan/mod.rs index 6a00e1727..f6fa9cae5 100644 --- a/clash_lib/src/proxy/trojan/mod.rs +++ b/clash_lib/src/proxy/trojan/mod.rs @@ -151,13 +151,15 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { + let (packet_mark, iface) = self.opts.common_opts.merge(sess); + let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .map_err(|x| { io::Error::new( @@ -191,13 +193,14 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { + let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .map_err(|x| { io::Error::new( diff --git a/clash_lib/src/proxy/vmess/mod.rs b/clash_lib/src/proxy/vmess/mod.rs index 178ddc1c7..1c70f9dab 100644 --- a/clash_lib/src/proxy/vmess/mod.rs +++ b/clash_lib/src/proxy/vmess/mod.rs @@ -169,14 +169,14 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - debug!("Connecting to {} via VMess", sess); + let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .map_err(|x| { io::Error::new( @@ -210,13 +210,14 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { + let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref(), + iface, #[cfg(any(target_os = "linux", target_os = "android"))] - None, + packet_mark, ) .map_err(|x| { io::Error::new( From 0541a407898120e1dfffb92f23479abea76f04f8 Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 19:40:27 +0800 Subject: [PATCH 3/7] refactor: allow set, get mark & iface --- clash_lib/src/lib.rs | 2 +- clash_lib/src/session.rs | 47 ++++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/clash_lib/src/lib.rs b/clash_lib/src/lib.rs index 793cbcaf3..367bb179c 100644 --- a/clash_lib/src/lib.rs +++ b/clash_lib/src/lib.rs @@ -33,11 +33,11 @@ mod common; mod config; mod proxy; mod session; - pub use config::def::Config as ClashConfigDef; pub use config::def::DNS as ClashDNSConfigDef; pub use config::DNSListen as ClashDNSListen; pub use config::RuntimeConfig as ClashRuntimeConfig; +pub use session::{get_iface, get_somark, set_iface, set_somark}; #[derive(Error, Debug)] pub enum Error { diff --git a/clash_lib/src/session.rs b/clash_lib/src/session.rs index 0ada4166e..0f1b7ad9b 100644 --- a/clash_lib/src/session.rs +++ b/clash_lib/src/session.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; +use std::ops::Deref; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use std::sync::RwLock; use std::{ io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, @@ -376,8 +378,6 @@ pub struct Session { pub source: SocketAddr, /// The proxy target address of a proxy connection. pub destination: SocksAddr, - /// The bind interface - pub iface: Option, } impl Session { @@ -408,7 +408,6 @@ impl Default for Session { typ: Type::Http, source: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), destination: SocksAddr::any_ipv4(), - iface: None, } } } @@ -429,7 +428,6 @@ impl Debug for Session { .field("network", &self.network) .field("source", &self.source) .field("destination", &self.destination) - .field("iface", &self.iface) .finish() } } @@ -441,7 +439,6 @@ impl Clone for Session { typ: self.typ, source: self.source, destination: self.destination.clone(), - iface: self.iface.as_ref().cloned(), } } } @@ -458,9 +455,17 @@ fn insuff_bytes() -> io::Error { io::Error::new(io::ErrorKind::Other, "insufficient bytes") } - static GLOBAL_MARK: AtomicU32 = AtomicU32::new(0); static ENABLE_MARK: AtomicBool = AtomicBool::new(false); +pub fn set_somark(mark: Option) { + match mark { + Some(mark) => { + GLOBAL_MARK.store(mark, Ordering::SeqCst); + ENABLE_MARK.store(true, Ordering::SeqCst); + } + None => ENABLE_MARK.store(false, Ordering::SeqCst), + } +} /// get socket SO_MARK that outgoing socket should use pub fn get_somark() -> Option { if ENABLE_MARK.load(Ordering::Relaxed) { @@ -468,4 +473,32 @@ pub fn get_somark() -> Option { } else { None } -} \ No newline at end of file +} + +static GLOBAL_IFACE: RwLock> = RwLock::new(None); +static ENABLE_IFACE: AtomicBool = AtomicBool::new(false); +pub fn set_iface(iface: Option) { + match iface { + Some(iface) => match GLOBAL_IFACE.write() { + Ok(mut guard) => { + *guard = Some(iface); + ENABLE_IFACE.store(true, Ordering::SeqCst); + } + // fail if the `RwLock` is poisoned. which only happens when writer holds lock and panics + Err(_) => ENABLE_IFACE.store(false, Ordering::SeqCst), + }, + None => ENABLE_IFACE.store(false, Ordering::SeqCst), + } +} +/// get iface that outgoing socket should use +pub fn get_iface() -> Option { + if ENABLE_IFACE.load(Ordering::Relaxed) { + match GLOBAL_IFACE.read() { + Ok(guard) => guard.deref().to_owned(), + // fail if the `RwLock` is poisoned. which only happens when writer holds lock and panics + _ => None, + } + } else { + None + } +} From a55436d7631dad618ab2c89e46eea3b81288608e Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 20:11:34 +0800 Subject: [PATCH 4/7] refactor: respect proxy node specific iface & mark --- clash_lib/src/proxy/converters/shadowsocks.rs | 2 +- clash_lib/src/proxy/direct/mod.rs | 14 +++------- clash_lib/src/proxy/mod.rs | 26 ++----------------- clash_lib/src/proxy/shadowsocks/mod.rs | 12 +++------ clash_lib/src/proxy/socks/inbound/stream.rs | 2 -- clash_lib/src/proxy/trojan/mod.rs | 13 +++------- clash_lib/src/proxy/utils/socket_helpers.rs | 26 ++++++++++--------- clash_lib/src/proxy/vmess/mod.rs | 13 +++------- 8 files changed, 33 insertions(+), 75 deletions(-) diff --git a/clash_lib/src/proxy/converters/shadowsocks.rs b/clash_lib/src/proxy/converters/shadowsocks.rs index b99573417..1e0afe6c7 100644 --- a/clash_lib/src/proxy/converters/shadowsocks.rs +++ b/clash_lib/src/proxy/converters/shadowsocks.rs @@ -2,7 +2,7 @@ use crate::{ config::internal::proxy::OutboundShadowsocks, proxy::{ shadowsocks::{Handler, HandlerOptions, OBFSOption}, - AnyOutboundHandler + AnyOutboundHandler, }, Error, }; diff --git a/clash_lib/src/proxy/direct/mod.rs b/clash_lib/src/proxy/direct/mod.rs index 8abf99ff1..34e339f1e 100644 --- a/clash_lib/src/proxy/direct/mod.rs +++ b/clash_lib/src/proxy/direct/mod.rs @@ -53,7 +53,6 @@ impl OutboundHandler for Handler { sess.destination.host().as_str(), sess.destination.port(), None, - #[cfg(any(target_os = "linux", target_os = "android"))] None, ) .await?; @@ -74,17 +73,12 @@ impl OutboundHandler for Handler { async fn connect_datagram( &self, - sess: &Session, + _sess: &Session, resolver: ThreadSafeDNSResolver, ) -> std::io::Result { - let d = new_udp_socket( - None, - sess.iface.as_ref(), - #[cfg(any(target_os = "linux", target_os = "android"))] - None, - ) - .await - .map(|x| OutboundDatagramImpl::new(x, resolver))?; + let d = new_udp_socket(None, None, None) + .await + .map(|x| OutboundDatagramImpl::new(x, resolver))?; let d = ChainedDatagramWrapper::new(d); d.append_to_chain(self.name()).await; diff --git a/clash_lib/src/proxy/mod.rs b/clash_lib/src/proxy/mod.rs index f143ac62b..7508c7cfd 100644 --- a/clash_lib/src/proxy/mod.rs +++ b/clash_lib/src/proxy/mod.rs @@ -88,34 +88,12 @@ pub type AnyOutboundDatagram = #[allow(dead_code)] #[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CommonOption { - #[serde(rename = "interface-name")] - so_mark: Option, #[serde(rename = "routing-mark")] + so_mark: Option, + #[serde(rename = "interface-name")] iface: Option, } -impl CommonOption { - pub fn merge<'a>(&'a self, sess: &'a Session) -> (Option, Option<&'a Interface>) { - let so_mark = if let Some(so_mark) = self.so_mark { - Some(so_mark) - } else if let Some(so_mark) = sess.packet_mark { - Some(so_mark) - } else { - None - }; - - let iface = if let Some(iface) = self.iface.as_ref() { - Some(iface) - } else if let Some(iface) = sess.iface.as_ref() { - Some(iface) - } else { - None - }; - - (so_mark, iface) - } -} - #[async_trait] pub trait InboundListener: Send + Sync + Unpin { /// support tcp or not diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index f0fecc960..6fa10c951 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -213,14 +213,12 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .map_err(|x| { io::Error::new( @@ -309,12 +307,10 @@ impl OutboundHandler for Handler { _ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported cipher")), }, ); - let (packet_mark, iface) = self.opts.common_opts.merge(sess); let socket = new_udp_socket( None, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .await?; let socket = ProxySocket::from_socket(UdpSocketType::Client, ctx, &cfg, socket); diff --git a/clash_lib/src/proxy/socks/inbound/stream.rs b/clash_lib/src/proxy/socks/inbound/stream.rs index d518c8ca9..53243a713 100644 --- a/clash_lib/src/proxy/socks/inbound/stream.rs +++ b/clash_lib/src/proxy/socks/inbound/stream.rs @@ -166,8 +166,6 @@ pub async fn handle_tcp<'a>( let sess = Session { network: Network::Udp, typ: Type::Socks5, - packet_mark: None, - iface: None, ..Default::default() }; diff --git a/clash_lib/src/proxy/trojan/mod.rs b/clash_lib/src/proxy/trojan/mod.rs index f6fa9cae5..96cc0c57d 100644 --- a/clash_lib/src/proxy/trojan/mod.rs +++ b/clash_lib/src/proxy/trojan/mod.rs @@ -151,15 +151,12 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let (packet_mark, iface) = self.opts.common_opts.merge(sess); - let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .map_err(|x| { io::Error::new( @@ -193,14 +190,12 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .map_err(|x| { io::Error::new( diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index 1f163db5c..700e049ad 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -14,7 +14,7 @@ use tokio::{ use tracing::warn; use super::Interface; -use crate::{app::dns::ThreadSafeDNSResolver, proxy::AnyStream}; +use crate::{app::dns::ThreadSafeDNSResolver, get_iface, proxy::AnyStream, session::get_somark}; pub fn apply_tcp_options(s: TcpStream) -> std::io::Result { #[cfg(not(target_os = "windows"))] @@ -71,8 +71,8 @@ pub async fn new_tcp_stream<'a>( resolver: ThreadSafeDNSResolver, address: &'a str, port: u16, - iface: Option<&'a Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + iface: Option<&Interface>, + packet_mark: Option, ) -> io::Result { let dial_addr = resolver .resolve(address, false) @@ -99,12 +99,13 @@ pub async fn new_tcp_stream<'a>( } }; - if let Some(iface) = iface { - must_bind_socket_on_interface(&socket, iface)?; + let global_iface = get_iface(); + if let Some(iface) = iface.or_else(|| global_iface.as_ref()) { + must_bind_socket_on_interface(&socket, &iface)?; } - #[cfg(any(target_os = "linux", target_os = "android"))] - if let Some(packet_mark) = packet_mark { + #[cfg(target_os = "linux")] + if let Some(packet_mark) = packet_mark.or_else(|| get_somark()) { socket.set_mark(packet_mark)?; } @@ -124,7 +125,7 @@ pub async fn new_tcp_stream<'a>( pub async fn new_udp_socket( src: Option<&SocketAddr>, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + packet_mark: Option, ) -> io::Result { let socket = match src { Some(src) => { @@ -141,12 +142,13 @@ pub async fn new_udp_socket( socket.bind(&(*src).into())?; } - if let Some(iface) = iface { - must_bind_socket_on_interface(&socket, iface)?; + let global_iface = get_iface(); + if let Some(iface) = iface.or_else(|| global_iface.as_ref()) { + must_bind_socket_on_interface(&socket, &iface)?; } - #[cfg(any(target_os = "linux", target_os = "android"))] - if let Some(packet_mark) = packet_mark { + #[cfg(target_os = "linux")] + if let Some(packet_mark) = packet_mark.or_else(|| get_somark()) { socket.set_mark(packet_mark)?; } diff --git a/clash_lib/src/proxy/vmess/mod.rs b/clash_lib/src/proxy/vmess/mod.rs index 1c70f9dab..e85b3672f 100644 --- a/clash_lib/src/proxy/vmess/mod.rs +++ b/clash_lib/src/proxy/vmess/mod.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, io, net::IpAddr, sync::Arc}; use async_trait::async_trait; use futures::TryFutureExt; -use tracing::debug; mod vmess_impl; @@ -169,14 +168,12 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver, self.opts.server.as_str(), self.opts.port, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .map_err(|x| { io::Error::new( @@ -210,14 +207,12 @@ impl OutboundHandler for Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let (packet_mark, iface) = self.opts.common_opts.merge(sess); let stream = new_tcp_stream( resolver.clone(), self.opts.server.as_str(), self.opts.port, - iface, - #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + self.opts.common_opts.iface.as_ref(), + self.opts.common_opts.so_mark, ) .map_err(|x| { io::Error::new( From 9d0de09752c2dccce00702000cc28f45a2041953 Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 20:21:18 +0800 Subject: [PATCH 5/7] style: make clippy happy --- clash_lib/src/app/dns/dhcp.rs | 1 - clash_lib/src/common/http.rs | 1 - clash_lib/src/proxy/http/inbound/connector.rs | 1 - clash_lib/src/proxy/http/inbound/proxy.rs | 2 -- clash_lib/src/proxy/relay/mod.rs | 1 - clash_lib/src/proxy/socks/inbound/stream.rs | 8 +----- clash_lib/src/proxy/tun/inbound.rs | 1 - clash_lib/src/proxy/utils/socket_helpers.rs | 26 +++++++++---------- clash_lib/src/proxy/wg/wireguard.rs | 8 +----- 9 files changed, 15 insertions(+), 34 deletions(-) diff --git a/clash_lib/src/app/dns/dhcp.rs b/clash_lib/src/app/dns/dhcp.rs index 50d08fd5f..6817e19c9 100644 --- a/clash_lib/src/app/dns/dhcp.rs +++ b/clash_lib/src/app/dns/dhcp.rs @@ -177,7 +177,6 @@ async fn listen_dhcp_client(iface: &str) -> io::Result { new_udp_socket( Some(&listen_addr.parse().expect("must parse")), Some(&Interface::Name(iface.to_string())), - #[cfg(any(target_os = "linux", target_os = "android"))] None, ) .await diff --git a/clash_lib/src/common/http.rs b/clash_lib/src/common/http.rs index 3fab427a9..f0890d362 100644 --- a/clash_lib/src/common/http.rs +++ b/clash_lib/src/common/http.rs @@ -50,7 +50,6 @@ impl Service for LocalConnector { }, }), None, - #[cfg(any(target_os = "linux", target_os = "android"))] None, ) .await diff --git a/clash_lib/src/proxy/http/inbound/connector.rs b/clash_lib/src/proxy/http/inbound/connector.rs index cf227f962..51d55a092 100644 --- a/clash_lib/src/proxy/http/inbound/connector.rs +++ b/clash_lib/src/proxy/http/inbound/connector.rs @@ -51,7 +51,6 @@ impl tower::Service for Connector { typ: Type::Http, source: src, destination: destination.ok_or(ProxyError::InvalidUrl(url.to_string()))?, - ..Default::default() }; tokio::spawn(async move { diff --git a/clash_lib/src/proxy/http/inbound/proxy.rs b/clash_lib/src/proxy/http/inbound/proxy.rs index 1438e2ed2..d90c87df9 100644 --- a/clash_lib/src/proxy/http/inbound/proxy.rs +++ b/clash_lib/src/proxy/http/inbound/proxy.rs @@ -65,8 +65,6 @@ async fn proxy( typ: Type::HttpConnect, source: src, destination: addr, - - ..Default::default() }; dispatcher.dispatch_stream(sess, upgraded).await diff --git a/clash_lib/src/proxy/relay/mod.rs b/clash_lib/src/proxy/relay/mod.rs index 39bb69539..1eec2d829 100644 --- a/clash_lib/src/proxy/relay/mod.rs +++ b/clash_lib/src/proxy/relay/mod.rs @@ -92,7 +92,6 @@ impl OutboundHandler for Handler { remote_addr.host().as_str(), remote_addr.port(), None, - #[cfg(any(target_os = "linux", target_os = "android"))] None, ) .await?; diff --git a/clash_lib/src/proxy/socks/inbound/stream.rs b/clash_lib/src/proxy/socks/inbound/stream.rs index 53243a713..dbca52583 100644 --- a/clash_lib/src/proxy/socks/inbound/stream.rs +++ b/clash_lib/src/proxy/socks/inbound/stream.rs @@ -138,13 +138,7 @@ pub async fn handle_tcp<'a>( } socks_command::UDP_ASSOCIATE => { let udp_addr = SocketAddr::new(s.local_addr()?.ip(), 0); - let udp_inbound = new_udp_socket( - Some(&udp_addr), - None, - #[cfg(any(target_os = "linux", target_os = "android"))] - None, - ) - .await?; + let udp_inbound = new_udp_socket(Some(&udp_addr), None, None).await?; trace!( "Got a UDP_ASSOCIATE request from {}, UDP assigned at {}", diff --git a/clash_lib/src/proxy/tun/inbound.rs b/clash_lib/src/proxy/tun/inbound.rs index 7e1a33393..128eba5b7 100644 --- a/clash_lib/src/proxy/tun/inbound.rs +++ b/clash_lib/src/proxy/tun/inbound.rs @@ -26,7 +26,6 @@ async fn handle_inbound_stream( typ: Type::Tun, source: local_addr, destination: remote_addr.into(), - ..Default::default() }; dispatcher.dispatch_stream(sess, stream).await; diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index 700e049ad..e03d012c3 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -14,7 +14,7 @@ use tokio::{ use tracing::warn; use super::Interface; -use crate::{app::dns::ThreadSafeDNSResolver, get_iface, proxy::AnyStream, session::get_somark}; +use crate::{app::dns::ThreadSafeDNSResolver, proxy::AnyStream}; pub fn apply_tcp_options(s: TcpStream) -> std::io::Result { #[cfg(not(target_os = "windows"))] @@ -67,12 +67,12 @@ fn must_bind_socket_on_interface(socket: &socket2::Socket, iface: &Interface) -> } } -pub async fn new_tcp_stream<'a>( +pub async fn new_tcp_stream( resolver: ThreadSafeDNSResolver, - address: &'a str, + address: &str, port: u16, iface: Option<&Interface>, - packet_mark: Option, + #[allow(unused_variables)] packet_mark: Option, ) -> io::Result { let dial_addr = resolver .resolve(address, false) @@ -99,13 +99,13 @@ pub async fn new_tcp_stream<'a>( } }; - let global_iface = get_iface(); - if let Some(iface) = iface.or_else(|| global_iface.as_ref()) { - must_bind_socket_on_interface(&socket, &iface)?; + let global_iface = crate::get_iface(); + if let Some(iface) = iface.or(global_iface.as_ref()) { + must_bind_socket_on_interface(&socket, iface)?; } #[cfg(target_os = "linux")] - if let Some(packet_mark) = packet_mark.or_else(|| get_somark()) { + if let Some(packet_mark) = packet_mark.or_else(crate::get_somark) { socket.set_mark(packet_mark)?; } @@ -125,7 +125,7 @@ pub async fn new_tcp_stream<'a>( pub async fn new_udp_socket( src: Option<&SocketAddr>, iface: Option<&Interface>, - packet_mark: Option, + #[allow(unused_variables)] packet_mark: Option, ) -> io::Result { let socket = match src { Some(src) => { @@ -142,13 +142,13 @@ pub async fn new_udp_socket( socket.bind(&(*src).into())?; } - let global_iface = get_iface(); - if let Some(iface) = iface.or_else(|| global_iface.as_ref()) { - must_bind_socket_on_interface(&socket, &iface)?; + let global_iface = crate::get_iface(); + if let Some(iface) = iface.or(global_iface.as_ref()) { + must_bind_socket_on_interface(&socket, iface)?; } #[cfg(target_os = "linux")] - if let Some(packet_mark) = packet_mark.or_else(|| get_somark()) { + if let Some(packet_mark) = packet_mark.or_else(crate::get_somark) { socket.set_mark(packet_mark)?; } diff --git a/clash_lib/src/proxy/wg/wireguard.rs b/clash_lib/src/proxy/wg/wireguard.rs index 2a1b51cee..6bd331731 100644 --- a/clash_lib/src/proxy/wg/wireguard.rs +++ b/clash_lib/src/proxy/wg/wireguard.rs @@ -80,13 +80,7 @@ impl WireguardTunnel { let remote_endpoint = config.remote_endpoint; - let udp = new_udp_socket( - None, - None, - #[cfg(any(target_os = "linux", target_os = "android"))] - None, - ) - .await?; + let udp = new_udp_socket(None, None, None).await?; Ok(Self { source_peer_ip: config.source_peer_ip, From 687279208c6cfdb68e36a34bfb7fef23a761e0fb Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 21:56:14 +0800 Subject: [PATCH 6/7] feat: add StdSocketExt --- clash_lib/src/proxy/direct/mod.rs | 4 ++-- clash_lib/src/proxy/utils/socket_helpers.rs | 22 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clash_lib/src/proxy/direct/mod.rs b/clash_lib/src/proxy/direct/mod.rs index 34e339f1e..2daa2a024 100644 --- a/clash_lib/src/proxy/direct/mod.rs +++ b/clash_lib/src/proxy/direct/mod.rs @@ -65,8 +65,8 @@ impl OutboundHandler for Handler { async fn proxy_stream( &self, s: AnyStream, - #[allow(unused_variables)] sess: &Session, - #[allow(unused_variables)] _resolver: ThreadSafeDNSResolver, + _sess: &Session, + _resolver: ThreadSafeDNSResolver, ) -> std::io::Result { Ok(s) } diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index e03d012c3..619c4300d 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -157,6 +157,28 @@ pub async fn new_udp_socket( UdpSocket::from_std(socket.into()) } +/// An extension to std::net::{UdpSocket, TcpStream} +pub trait StdSocketExt { + fn set_mark(&self, mark: u32) -> io::Result<()>; +} +impl StdSocketExt for std::net::UdpSocket { + fn set_mark(&self, mark: u32) -> io::Result<()> { + set_mark(socket2::SockRef::from(self), mark) + } +} +impl StdSocketExt for std::net::TcpStream { + fn set_mark(&self, mark: u32) -> io::Result<()> { + set_mark(socket2::SockRef::from(self), mark) + } +} + +#[allow(unused_variables)] +fn set_mark(socket: socket2::SockRef<'_>, mark: u32) -> io::Result<()> { + #[cfg(target_os = "linux")] + return socket.set_mark(mark); + #[cfg(not(target_os = "linux"))] + return Ok(()); +} #[cfg(test)] mod tests { From 1a93175d0ce1e7d469265b4486873a296111c6a4 Mon Sep 17 00:00:00 2001 From: iHsin Date: Tue, 9 Apr 2024 22:41:48 +0800 Subject: [PATCH 7/7] chore: use 0xff by default --- clash_lib/src/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clash_lib/src/session.rs b/clash_lib/src/session.rs index 0f1b7ad9b..bb593033c 100644 --- a/clash_lib/src/session.rs +++ b/clash_lib/src/session.rs @@ -455,7 +455,7 @@ fn insuff_bytes() -> io::Error { io::Error::new(io::ErrorKind::Other, "insufficient bytes") } -static GLOBAL_MARK: AtomicU32 = AtomicU32::new(0); +static GLOBAL_MARK: AtomicU32 = AtomicU32::new(0xff); static ENABLE_MARK: AtomicBool = AtomicBool::new(false); pub fn set_somark(mark: Option) { match mark {