diff --git a/.cargo/config b/.cargo/config index bfc0c5e65..7885b5964 100644 --- a/.cargo/config +++ b/.cargo/config @@ -3,4 +3,5 @@ target-dir = "target" rustflags = ["--cfg", "tokio_unstable"] [env] -BORING_BSSL_SOURCE_PATH = { value = "deps/boringssl/src", relative = true} \ No newline at end of file +BORING_BSSL_SOURCE_PATH = { value = "deps/boringssl/src", relative = true} +RUST_LOG= { value = "clash=trace" } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 472432d3b..f0599672c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2227,7 +2227,7 @@ checksum = "9252111cf132ba0929b6f8e030cac2a24b507f3a4d6db6fb2896f27b354c714b" [[package]] name = "netstack-lwip" version = "0.3.4" -source = "git+https://github.com/Watfaq/netstack-lwip.git?rev=4fef23#4fef237a9c8f4a94afaa05a3d0e9c57bd0ae8754" +source = "git+https://github.com/Watfaq/netstack-lwip.git?rev=8c8c0b0#8c8c0b0646ebeb6eb84821d95b7261d3e00d94dd" dependencies = [ "anyhow", "bindgen 0.59.2", @@ -3720,7 +3720,7 @@ dependencies = [ [[package]] name = "trust-dns-client" version = "0.23.0" -source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=3386076#3386076e7c670e7a7e35d65dba1830a48cd815b5" dependencies = [ "cfg-if", "data-encoding", @@ -3787,7 +3787,7 @@ dependencies = [ [[package]] name = "trust-dns-proto" version = "0.23.0" -source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=3386076#3386076e7c670e7a7e35d65dba1830a48cd815b5" dependencies = [ "async-trait", "bytes", @@ -3839,7 +3839,7 @@ dependencies = [ [[package]] name = "trust-dns-resolver" version = "0.23.0" -source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=3386076#3386076e7c670e7a7e35d65dba1830a48cd815b5" dependencies = [ "cfg-if", "futures-util", @@ -3862,7 +3862,7 @@ dependencies = [ [[package]] name = "trust-dns-server" version = "0.23.0" -source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=3386076#3386076e7c670e7a7e35d65dba1830a48cd815b5" dependencies = [ "async-trait", "bytes", diff --git a/clash/tests/data/config/rules.yaml b/clash/tests/data/config/rules.yaml index aa8f600ad..17013c545 100644 --- a/clash/tests/data/config/rules.yaml +++ b/clash/tests/data/config/rules.yaml @@ -38,9 +38,9 @@ dns: # involved. Clash answers the DNS question with the first result gathered. nameserver: - 114.114.114.114 # default value - - 8.8.8.8 # default value - - tls://dns.google:853 # DNS over TLS - - https://dns.google/dns-query # DNS over HTTPS + - 1.1.1.1 # default value + - tls://1.1.1.1:853 # DNS over TLS + - https://1.1.1.1/dns-query # DNS over HTTPS # - dhcp://en0 # dns from dhcp allow-lan: true @@ -146,16 +146,23 @@ proxies: path: /api/v3/download.getFile headers: Host: 5607b9d187e655736f563fee87d7283994721.laowanxiang.com + - name: "ss-simple" + type: ss + server: 10.0.0.13 + port: 8388 + cipher: aes-256-gcm + password: "password" + udp: true proxy-providers: file-provider: type: file path: ./ss.yaml - interval: 5 + interval: 300 health-check: enable: true url: http://www.gstatic.com/generate_204 - interval: 5 + interval: 300 rules: - DOMAIN,ipinfo.io,relay @@ -166,7 +173,7 @@ rules: - DOMAIN,google.com,select - SRC-IP-CIDR,192.168.1.1/24,DIRECT - GEOIP,CN,DIRECT - - DST-PORT,53,plain-vmess + - DST-PORT,53,ss-simple - SRC-PORT,7777,DIRECT - MATCH, DIRECT ... diff --git a/clash_lib/Cargo.toml b/clash_lib/Cargo.toml index d44539010..660df71b6 100644 --- a/clash_lib/Cargo.toml +++ b/clash_lib/Cargo.toml @@ -52,17 +52,17 @@ axum = { version = "0.6.20", features = ["ws"] } tower-http = { version = "0.4.0", features = ["fs", "trace", "cors"] } chrono = { version = "0.4.26", features = ["serde"] } tun = { git = "https://github.com/Watfaq/rust-tun.git", rev = "5c0702b", features = ["async"] } -netstack-lwip = { git = "https://github.com/Watfaq/netstack-lwip.git", rev = "4fef23" } +netstack-lwip = { git = "https://github.com/Watfaq/netstack-lwip.git", rev = "8c8c0b0" } serde = { version = "1.0", features=["derive"] } serde_yaml = "0.9" erased-serde = "0.3.30" -trust-dns-client = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2" } -trust-dns-resolver = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2" } -trust-dns-server = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2", features = ["dns-over-rustls", "dns-over-https-rustls"] } -trust-dns-proto = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2", features = ["dns-over-rustls", "dns-over-https-rustls"] } +trust-dns-client = { git = "https://github.com/Watfaq/trust-dns.git", rev = "3386076" } +trust-dns-resolver = { git = "https://github.com/Watfaq/trust-dns.git", rev = "3386076" } +trust-dns-server = { git = "https://github.com/Watfaq/trust-dns.git", rev = "3386076", features = ["dns-over-rustls", "dns-over-https-rustls"] } +trust-dns-proto = { git = "https://github.com/Watfaq/trust-dns.git", rev = "3386076", features = ["dns-over-rustls", "dns-over-https-rustls"] } # trust-dns-resolver = "0.23" # trust-dns-server = { version = "0.23", features = ["dns-over-rustls", "dns-over-https-rustls"] } diff --git a/clash_lib/src/app/api/handlers/connection.rs b/clash_lib/src/app/api/handlers/connection.rs index f6e335f3d..e7089922f 100644 --- a/clash_lib/src/app/api/handlers/connection.rs +++ b/clash_lib/src/app/api/handlers/connection.rs @@ -9,7 +9,7 @@ use axum::{ use http::{HeaderMap, Request}; use hyper::{body::HttpBody, Body}; use serde::Deserialize; -use tracing::warn; +use tracing::{debug, warn}; use crate::app::{ api::{handlers::utils::is_request_websocket, AppState}, @@ -72,7 +72,8 @@ async fn get_connections( let body = String::from_utf8(j.to_vec()).unwrap(); if let Err(e) = socket.send(Message::Text(body)).await { - warn!("ws send error: {}", e); + // likely client gone + debug!("ws send error: {}", e); break; } diff --git a/clash_lib/src/app/dispatcher/dispatcher.rs b/clash_lib/src/app/dispatcher/dispatcher.rs index 515f588af..1e783b2bc 100644 --- a/clash_lib/src/app/dispatcher/dispatcher.rs +++ b/clash_lib/src/app/dispatcher/dispatcher.rs @@ -12,12 +12,16 @@ use futures::SinkExt; use futures::StreamExt; use std::collections::HashMap; use std::fmt::{Debug, Formatter}; +use std::net::SocketAddr; use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; use tokio::io::{copy_bidirectional, AsyncRead, AsyncWrite, AsyncWriteExt}; use tokio::sync::Mutex; use tokio::task::JoinHandle; +use tracing::instrument; +use tracing::trace; use tracing::{debug, error, info, warn}; -use tracing::{event, instrument}; use crate::app::dns::ThreadSafeDNSResolver; @@ -159,7 +163,7 @@ impl Dispatcher { sess: Session, udp_inbound: AnyInboundDatagram, ) -> tokio::sync::oneshot::Sender { - let outbound_handle_guard = Arc::new(Mutex::new(OutboundHandleMap::new())); + let outbound_handle_guard = TimeoutUdpSessionManager::new(); let router = self.router.clone(); let outbound_manager = self.outbound_manager.clone(); @@ -177,14 +181,18 @@ impl Dispatcher { sess.source = packet.src_addr.clone().must_into_socket_addr(); sess.destination = packet.dst_addr.clone(); + // populate fake ip for route matching let sess = if resolver.fake_ip_enabled() { + trace!("fake ip enabled"); match sess.destination { crate::session::SocksAddr::Ip(addr) => { let ip = addr.ip(); if resolver.is_fake_ip(ip).await { + trace!("fake ip detected"); let host = resolver.reverse_lookup(ip).await; match host { Some(host) => { + trace!("fake ip resolved to {}", host); let mut sess = sess; sess.destination = crate::session::SocksAddr::Domain(host, addr.port()); @@ -205,6 +213,10 @@ impl Dispatcher { sess }; + // mutate packet for fake ip + let mut packet = packet; + packet.dst_addr = sess.destination.clone(); + let mode = mode.lock().await; info!("dispatching {} with mode {}", sess, mode); let (outbound_name, rule) = match *mode { @@ -223,9 +235,13 @@ impl Dispatcher { .get_outbound(&outbound_name) .expect(format!("unknown rule: {}", outbound_name).as_str()); - let mut outbound_handle_guard = outbound_handle_guard.lock().await; - - match outbound_handle_guard.get_outbound_sender_mut(&outbound_name) { + match outbound_handle_guard + .get_outbound_sender_mut( + &outbound_name, + packet.src_addr.clone().must_into_socket_addr(), // this is only expected to be socket addr as it's from local udp + ) + .await + { None => { let outbound_datagram = match handler.connect_datagram(&sess, resolver.clone()).await { @@ -255,6 +271,7 @@ impl Dispatcher { while let Some(packet) = remote_r.next().await { // NAT let mut packet = packet; + packet.src_addr = sess.destination.clone().into(); packet.dst_addr = sess.source.into(); debug!("UDP NAT for packet: {:?}, session: {}", packet, sess); @@ -280,14 +297,15 @@ impl Dispatcher { } }); - outbound_handle_guard.insert( - &outbound_name, - r_handle, - w_handle, - remote_sender.clone(), - ); - - drop(outbound_handle_guard); + outbound_handle_guard + .insert( + &outbound_name, + packet.src_addr.clone().must_into_socket_addr(), + r_handle, + w_handle, + remote_sender.clone(), + ) + .await; match remote_sender.send(packet.clone()).await { Ok(_) => {} @@ -333,8 +351,98 @@ impl Dispatcher { } } -type OutBoundPacketSender = tokio::sync::mpsc::Sender; // outbound packet sender -struct OutboundHandleMap(HashMap, JoinHandle<()>, OutBoundPacketSender)>); +type OutboundPacketSender = tokio::sync::mpsc::Sender; // outbound packet sender + +struct TimeoutUdpSessionManager { + map: Arc>, + + cleaner: Option>, +} + +impl Drop for TimeoutUdpSessionManager { + fn drop(&mut self) { + trace!("dropping timeout udp session manager"); + self.cleaner.take().map(|x| x.abort()); + } +} + +impl TimeoutUdpSessionManager { + fn new() -> Self { + let map = Arc::new(Mutex::new(OutboundHandleMap::new())); + let timeout = Duration::from_secs(10); + + let map_cloned = map.clone(); + + let cleaner = tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(10)).await; + + trace!("timeout udp session cleaner scanning"); + let mut g = map_cloned.lock().await; + let mut alived = 0; + let mut expired = 0; + g.0.retain(|k, x| { + let (h1, h2, _, last) = x; + let now = Instant::now(); + let alive = now.duration_since(*last) < timeout; + if !alive { + expired += 1; + trace!("udp session expired: {:?}", k); + h1.abort(); + h2.abort(); + } else { + alived += 1; + } + alive + }); + trace!( + "timeout udp session cleaner finished, alived: {}, expired: {}", + alived, + expired + ); + } + }); + + Self { + map, + + cleaner: Some(cleaner), + } + } + + async fn insert( + &self, + outbound_name: &str, + src_addr: SocketAddr, + recv_handle: JoinHandle<()>, + send_handle: JoinHandle<()>, + sender: OutboundPacketSender, + ) { + let mut map = self.map.lock().await; + map.insert(outbound_name, src_addr, recv_handle, send_handle, sender); + } + + async fn get_outbound_sender_mut( + &self, + outbound_name: &str, + src_addr: SocketAddr, + ) -> Option { + let mut map = self.map.lock().await; + map.get_outbound_sender_mut(outbound_name, src_addr) + } +} + +struct OutboundHandleMap( + HashMap< + (String, SocketAddr), + ( + JoinHandle<()>, + JoinHandle<()>, + OutboundPacketSender, + Instant, + ), + >, +); impl OutboundHandleMap { fn new() -> Self { @@ -344,28 +452,42 @@ impl OutboundHandleMap { fn insert( &mut self, outbound_name: &str, + src_addr: SocketAddr, recv_handle: JoinHandle<()>, send_handle: JoinHandle<()>, - sender: OutBoundPacketSender, + sender: OutboundPacketSender, ) { self.0.insert( - outbound_name.to_string(), - (recv_handle, send_handle, sender), + (outbound_name.to_string(), src_addr), + (recv_handle, send_handle, sender, Instant::now()), ); } fn get_outbound_sender_mut( &mut self, outbound_name: &str, - ) -> Option<&mut OutBoundPacketSender> { - self.0.get_mut(outbound_name).map(|(_, _, sender)| sender) + src_addr: SocketAddr, + ) -> Option { + self.0 + .get_mut(&(outbound_name.to_owned(), src_addr)) + .map(|(_, _, sender, last)| { + trace!( + "updating last access time for outbound {:?}", + (outbound_name, src_addr) + ); + *last = Instant::now(); + sender.clone() + }) } } impl Drop for OutboundHandleMap { fn drop(&mut self) { - debug!("dropping outbound handle map"); - for (_, (recv_handle, send_handle, _)) in self.0.drain() { + trace!( + "dropping inner outbound handle map that has {} sessions", + self.0.len() + ); + for (_, (recv_handle, send_handle, _, _)) in self.0.drain() { recv_handle.abort(); send_handle.abort(); } diff --git a/clash_lib/src/app/dispatcher/statistics_manager.rs b/clash_lib/src/app/dispatcher/statistics_manager.rs index 2d38ec64f..0e41e99a6 100644 --- a/clash_lib/src/app/dispatcher/statistics_manager.rs +++ b/clash_lib/src/app/dispatcher/statistics_manager.rs @@ -173,6 +173,7 @@ impl Manager { } } + #[allow(dead_code)] pub fn reset_statistic(&self) { self.upload_temp.store(0, Ordering::Relaxed); self.upload_blip.store(0, Ordering::Relaxed); diff --git a/clash_lib/src/app/dns/dhcp.rs b/clash_lib/src/app/dns/dhcp.rs index 2a50cb7b3..590f8ae57 100644 --- a/clash_lib/src/app/dns/dhcp.rs +++ b/clash_lib/src/app/dns/dhcp.rs @@ -49,6 +49,10 @@ impl Debug for DhcpClient { #[async_trait] impl Client for DhcpClient { + fn id(&self) -> String { + format!("dhcp#{}", self.iface) + } + async fn exchange(&self, msg: &Message) -> anyhow::Result { let clients = self.resolve().await?; let mut dbg_str = vec![]; diff --git a/clash_lib/src/app/dns/dns_client.rs b/clash_lib/src/app/dns/dns_client.rs index 987f16f1d..87bd627fa 100644 --- a/clash_lib/src/app/dns/dns_client.rs +++ b/clash_lib/src/app/dns/dns_client.rs @@ -291,6 +291,10 @@ impl Debug for DnsClient { #[async_trait] impl Client for DnsClient { + fn id(&self) -> String { + format!("{}#{}:{}", &self.net, &self.host, &self.port) + } + async fn exchange(&self, msg: &Message) -> anyhow::Result { let mut req = DnsRequest::new(msg.clone(), DnsRequestOptions::default()); req.set_id(rand::random::()); diff --git a/clash_lib/src/app/dns/mod.rs b/clash_lib/src/app/dns/mod.rs index 374ad0fa7..3dbc384f0 100644 --- a/clash_lib/src/app/dns/mod.rs +++ b/clash_lib/src/app/dns/mod.rs @@ -48,6 +48,8 @@ macro_rules! dns_warn { #[async_trait] pub trait Client: Sync + Send + Debug { + /// used to identify the client for logging + fn id(&self) -> String; async fn exchange(&self, msg: &op::Message) -> anyhow::Result; } @@ -84,12 +86,6 @@ pub trait ClashResolver: Sync + Send { async fn reverse_lookup(&self, ip: std::net::IpAddr) -> Option; async fn is_fake_ip(&self, ip: std::net::IpAddr) -> bool; async fn fake_ip_exists(&self, ip: std::net::IpAddr) -> bool; - async fn lookup_fake_ip(&self, host: &str) -> Option { - todo!(); - } - async fn generate_fake_ip_packet(&self, data: Vec) -> anyhow::Result { - todo!(); - } fn ipv6(&self) -> bool; fn set_ipv6(&self, enable: bool); diff --git a/clash_lib/src/app/dns/resolver.rs b/clash_lib/src/app/dns/resolver.rs index eaade84b8..b86ddfaf9 100644 --- a/clash_lib/src/app/dns/resolver.rs +++ b/clash_lib/src/app/dns/resolver.rs @@ -180,7 +180,9 @@ impl Resolver { queries.push( async move { c.exchange(message) - .inspect_err(|x| warn!("DNS resolve error: {}", x.to_string())) + .inspect_err(|x| { + warn!("DNS client {} resolve error: {}", c.id(), x.to_string()) + }) .await } .boxed(), diff --git a/clash_lib/src/proxy/datagram.rs b/clash_lib/src/proxy/datagram.rs index c2e40793d..804631267 100644 --- a/clash_lib/src/proxy/datagram.rs +++ b/clash_lib/src/proxy/datagram.rs @@ -8,7 +8,6 @@ use std::fmt::{Debug, Display, Formatter}; use std::io; use std::net::SocketAddr; use std::pin::Pin; -use std::sync::{Mutex, RwLock}; use std::task::{Context, Poll}; use tokio::io::ReadBuf; use tokio::net::UdpSocket; @@ -53,7 +52,7 @@ impl UdpPacket { } pub struct InboundUdp { - inner: Mutex, + inner: I, } impl InboundUdp @@ -62,9 +61,7 @@ where I: Sink<((Bytes, SocksAddr), SocketAddr)>, { pub fn new(inner: I) -> Self { - Self { - inner: Mutex::new(inner), - } + Self { inner } } } @@ -79,25 +76,20 @@ impl Stream for InboundUdp> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); - match pin.inner.try_lock() { - Ok(mut guard) => match guard.poll_next_unpin(cx) { - Poll::Ready(item) => match item { - None => Poll::Ready(None), - Some(item) => match item { - Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket { - data: pkt.to_vec(), - src_addr: SocksAddr::Ip(src), - dst_addr: dst, - })), - Err(_) => Poll::Ready(None), - }, + + match pin.inner.poll_next_unpin(cx) { + Poll::Ready(item) => match item { + None => Poll::Ready(None), + Some(item) => match item { + Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket { + data: pkt.to_vec(), + src_addr: SocksAddr::Ip(src), + dst_addr: dst, + })), + Err(_) => Poll::Ready(None), }, - Poll::Pending => Poll::Pending, - }, - Err(err) => match err { - std::sync::TryLockError::WouldBlock => Poll::Pending, - std::sync::TryLockError::Poisoned(_) => Poll::Ready(None), }, + Poll::Pending => Poll::Pending, } } } @@ -107,12 +99,12 @@ impl Sink for InboundUdp> { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); - pin.inner.lock().expect("lock error").poll_ready_unpin(cx) + pin.inner.poll_ready_unpin(cx) } fn start_send(self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { let pin = self.get_mut(); - pin.inner.lock().expect("lock error").start_send_unpin(( + pin.inner.start_send_unpin(( (item.data.into(), item.src_addr), item.dst_addr.must_into_socket_addr(), )) @@ -120,12 +112,12 @@ impl Sink for InboundUdp> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); - pin.inner.lock().expect("lock error").poll_flush_unpin(cx) + pin.inner.poll_flush_unpin(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); - pin.inner.lock().expect("lock error").poll_close_unpin(cx) + pin.inner.poll_close_unpin(cx) } } @@ -133,7 +125,7 @@ impl InboundDatagram for InboundUdp> {} #[must_use = "sinks do nothing unless polled"] pub struct OutboundDatagramImpl { - inner: RwLock, + inner: UdpSocket, resolver: ThreadSafeDNSResolver, flushed: bool, pkt: Option, @@ -142,7 +134,7 @@ pub struct OutboundDatagramImpl { impl OutboundDatagramImpl { pub fn new(udp: UdpSocket, resolver: ThreadSafeDNSResolver) -> AnyOutboundDatagram { let s = Self { - inner: RwLock::new(udp), + inner: udp, resolver, flushed: true, pkt: None, @@ -184,13 +176,14 @@ impl Sink for OutboundDatagramImpl { .. } = *self; - if let Some(pkt) = pkt.take() { - let dst = pkt.dst_addr; - let data = pkt.data; + if pkt.is_some() { + let p = pkt.as_ref().unwrap(); + let dst = &p.dst_addr; + let data = &p.data; let dst = match dst { SocksAddr::Domain(domain, port) => { let domain = domain.to_string(); - let port = port as u16; + let port = *port as u16; let mut fut = resolver.resolve(domain.as_str(), false); let ip = ready!(fut.as_mut().poll(cx).map_err(|_| { io::Error::new(io::ErrorKind::Other, "resolve domain failed") @@ -204,14 +197,10 @@ impl Sink for OutboundDatagramImpl { ))); } } - SocksAddr::Ip(addr) => addr, + SocksAddr::Ip(addr) => *addr, }; - let n = - ready!(inner - .write() - .expect("lock error") - .poll_send_to(cx, data.as_slice(), dst))?; + let n = ready!(inner.poll_send_to(cx, data.as_slice(), dst))?; let wrote_all = n == data.len(); self.pkt = None; self.flushed = true; @@ -245,11 +234,7 @@ impl Stream for OutboundDatagramImpl { let Self { ref mut inner, .. } = *self; let mut mem = vec![0u8; 65535]; let mut buf = ReadBuf::new(&mut mem); - match ready!(inner - .read() - .expect("lock error") - .poll_recv_from(cx, &mut buf)) - { + match ready!(inner.poll_recv_from(cx, &mut buf)) { Ok(src) => { let data = buf.filled().to_vec(); Poll::Ready(Some(UdpPacket { diff --git a/clash_lib/src/proxy/shadowsocks/datagram.rs b/clash_lib/src/proxy/shadowsocks/datagram.rs index 7be13c20a..fbe0cf217 100644 --- a/clash_lib/src/proxy/shadowsocks/datagram.rs +++ b/clash_lib/src/proxy/shadowsocks/datagram.rs @@ -7,7 +7,7 @@ use std::{ use futures::{ready, Sink, Stream}; use shadowsocks::ProxySocket; use tokio::io::ReadBuf; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::{ app::dns::ThreadSafeDNSResolver, @@ -90,6 +90,7 @@ impl Sink for OutboundDatagramShadowsocks { }))?; if let Some(ip) = ip { + trace!("resolve domain {} to {}", domain, ip); (ip, port).into() } else { return Poll::Ready(Err(io::Error::new( diff --git a/clash_lib/src/proxy/tun/datagram.rs b/clash_lib/src/proxy/tun/datagram.rs index f81622ded..b4759fdb7 100644 --- a/clash_lib/src/proxy/tun/datagram.rs +++ b/clash_lib/src/proxy/tun/datagram.rs @@ -1,4 +1,4 @@ -use std::task::Poll; +use std::{net::SocketAddr, task::Poll}; use futures::{ready, Sink, Stream}; @@ -14,6 +14,8 @@ pub struct TunDatagram { pkt: Option, flushed: bool, + #[allow(unused)] + local_addr: SocketAddr, } impl TunDatagram { @@ -22,12 +24,15 @@ impl TunDatagram { tx: tokio::sync::mpsc::Sender, // receive from tun rx: tokio::sync::mpsc::Receiver, + // the address of the tun udp socket + local_addr: SocketAddr, ) -> Self { Self { rx, tx, pkt: None, flushed: true, + local_addr, } } } diff --git a/clash_lib/src/proxy/tun/inbound.rs b/clash_lib/src/proxy/tun/inbound.rs index 5842b62fd..421a98b02 100644 --- a/clash_lib/src/proxy/tun/inbound.rs +++ b/clash_lib/src/proxy/tun/inbound.rs @@ -2,7 +2,7 @@ use super::{datagram::TunDatagram, netstack}; use std::{net::SocketAddr, sync::Arc}; use futures::{SinkExt, StreamExt}; -use tracing::{error, info, warn}; +use tracing::{error, info, trace, warn}; use tun::{Device, TunPacket}; use url::Url; @@ -47,7 +47,9 @@ async fn handle_inbound_datagram( dispatcher: Arc, resolver: ThreadSafeDNSResolver, ) { + let local_addr = socket.local_addr(); // tun i/o + let (ls, mut lr) = socket.split(); let ls = Arc::new(ls); @@ -59,7 +61,7 @@ async fn handle_inbound_datagram( // for dispatcher - the dispatcher would receive packets from this channel, which is from the stack // and send back packets to this channel, which is to the tun - let udp_stream = TunDatagram::new(l_tx, d_rx); + let udp_stream = TunDatagram::new(l_tx, d_rx, local_addr); let sess = Session { network: Network::Udp, @@ -72,14 +74,21 @@ async fn handle_inbound_datagram( // dispatcher -> tun tokio::spawn(async move { while let Some(pkt) = l_rx.recv().await { + trace!("tun <- dispatcher: {:?}", pkt); + // populate the correct src_addr, though is it necessary? let src_addr = match pkt.src_addr { SocksAddr::Ip(ip) => ip, SocksAddr::Domain(host, port) => { - if let Some(ip) = resolver.lookup_fake_ip(&host).await { - (ip, port).into() - } else { - warn!("failed to resolve fake ip: {}", host); - continue; + match resolver.resolve(&host, resolver.fake_ip_enabled()).await { + Ok(Some(ip)) => (ip, port).into(), + Ok(None) => { + warn!("failed to resolve domain: {}", host); + continue; + } + Err(e) => { + warn!("failed to resolve domain: {}", e); + continue; + } } } }; @@ -93,7 +102,7 @@ async fn handle_inbound_datagram( } }); - // tun -> dispatcher + // tun -> dispatcher tokio::spawn(async move { while let Ok((data, src_addr, dst_addr)) = lr.recv_from().await { let pkt = UdpPacket { @@ -102,6 +111,8 @@ async fn handle_inbound_datagram( dst_addr: dst_addr.into(), }; + trace!("tun -> dispatcher: {:?}", pkt); + match d_tx.send(pkt).await { Ok(_) => {} Err(e) => { @@ -154,7 +165,7 @@ pub fn get_runner( let network = cfg .network .as_ref() - .unwrap_or(&"198.18.0.0/16".to_owned()) + .unwrap_or(&"198.19.0.0/16".to_owned()) .parse::()?; tun_cfg diff --git a/scripts/check_socks5.py b/scripts/check_socks5.py index dda6ca8d4..f419309cc 100644 --- a/scripts/check_socks5.py +++ b/scripts/check_socks5.py @@ -6,7 +6,9 @@ def check_socks5_tun(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) req = b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x05\x62\x61\x69\x64\x75\x03\x63\x6f\x6d\x00\x00\x01\x00\x01" - s.sendto(req, ("8.8.8.8", 53)) + s.sendto(req, ("198.18.0.2", 53)) + + print(s.getsockname()) (res, address) = s.recvfrom(4096) if res[0] == req[0] and res[1] == req[1]: