diff --git a/clash/src/main.rs b/clash/src/main.rs index 44c9f70ac..c95f1f601 100644 --- a/clash/src/main.rs +++ b/clash/src/main.rs @@ -44,7 +44,8 @@ fn main() { .to_string(); if !Path::new(&file).exists() { - // TODO: offer a internal default config, to compatible with clash behavior + // TODO: offer a internal default config, to compatible with clash + // behavior panic!("config file not found: {}", file); } if cli.test_config { @@ -54,7 +55,7 @@ fn main() { exit(0); } Err(e) => { - eprintln!(" configuration file {} test failed: {}", file, e); + eprintln!("configuration file {} test failed: {}", file, e); exit(1); } } diff --git a/clash_lib/src/app/api/handlers/config.rs b/clash_lib/src/app/api/handlers/config.rs index c25c5ce3f..6927c98c7 100644 --- a/clash_lib/src/app/api/handlers/config.rs +++ b/clash_lib/src/app/api/handlers/config.rs @@ -144,7 +144,9 @@ async fn update_configs( .into_response(), } } - (None, None) => (StatusCode::BAD_REQUEST, "no path or payload provided").into_response(), + (None, None) => { + (StatusCode::BAD_REQUEST, "no path or payload provided").into_response() + } } } @@ -179,7 +181,10 @@ async fn patch_configs( Json(payload): Json, ) -> impl IntoResponse { if payload.allow_lan.is_some() { - warn!("setting allow_lan doesn't do anything. please set bind_address to a LAN address instead."); + warn!( + "setting allow_lan doesn't do anything. please set bind_address to a \ + LAN address instead." + ); } let mut inbound_manager = state.inbound_manager.lock().await; diff --git a/clash_lib/src/app/api/handlers/connection.rs b/clash_lib/src/app/api/handlers/connection.rs index 6baee68a8..23e93f782 100644 --- a/clash_lib/src/app/api/handlers/connection.rs +++ b/clash_lib/src/app/api/handlers/connection.rs @@ -2,7 +2,9 @@ use std::sync::Arc; use axum::{ body::Body, - extract::{ws::Message, FromRequest, Path, Query, Request, State, WebSocketUpgrade}, + extract::{ + ws::Message, FromRequest, Path, Query, Request, State, WebSocketUpgrade, + }, response::IntoResponse, routing::{delete, get}, Json, Router, @@ -72,7 +74,10 @@ async fn get_connections( break; } - tokio::time::sleep(tokio::time::Duration::from_secs(interval.unwrap_or(1))).await; + tokio::time::sleep(tokio::time::Duration::from_secs( + interval.unwrap_or(1), + )) + .await; } }) } @@ -86,7 +91,9 @@ async fn close_connection( format!("connection {} closed", id).into_response() } -async fn close_all_connection(State(state): State) -> impl IntoResponse { +async fn close_all_connection( + State(state): State, +) -> impl IntoResponse { let mgr = state.statistics_manager; mgr.close_all().await; "all connections closed".into_response() diff --git a/clash_lib/src/app/api/handlers/provider.rs b/clash_lib/src/app/api/handlers/provider.rs index b22448301..291669698 100644 --- a/clash_lib/src/app/api/handlers/provider.rs +++ b/clash_lib/src/app/api/handlers/provider.rs @@ -2,8 +2,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; use axum::{ extract::{Path, Query, State}, - http::Request, - http::StatusCode, + http::{Request, StatusCode}, middleware::{self, Next}, response::{IntoResponse, Response}, routing::get, @@ -11,11 +10,13 @@ use axum::{ }; use serde::Deserialize; -use crate::app::{ - api::AppState, outbound::manager::ThreadSafeOutboundManager, - remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, +use crate::{ + app::{ + api::AppState, outbound::manager::ThreadSafeOutboundManager, + remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, + }, + proxy::AnyOutboundHandler, }; -use crate::proxy::AnyOutboundHandler; #[derive(Clone)] struct ProviderState { outbound_manager: ThreadSafeOutboundManager, @@ -59,8 +60,9 @@ async fn get_providers(State(state): State) -> impl IntoResponse for (name, p) in outbound_manager.get_proxy_providers() { let p = p.read().await; let proxies = p.proxies().await; - let proxies = - futures::future::join_all(proxies.iter().map(|x| outbound_manager.get_proxy(x))); + let proxies = futures::future::join_all( + proxies.iter().map(|x| outbound_manager.get_proxy(x)), + ); let mut m = p.as_map().await; m.insert("proxies".to_owned(), Box::new(proxies.await)); providers.insert(name, m); diff --git a/clash_lib/src/app/api/handlers/proxy.rs b/clash_lib/src/app/api/handlers/proxy.rs index 15fe006e9..d7a0672ce 100644 --- a/clash_lib/src/app/api/handlers/proxy.rs +++ b/clash_lib/src/app/api/handlers/proxy.rs @@ -14,7 +14,8 @@ use serde::Deserialize; use crate::{ app::{ - api::AppState, outbound::manager::ThreadSafeOutboundManager, profile::ThreadSafeCacheFile, + api::AppState, outbound::manager::ThreadSafeOutboundManager, + profile::ThreadSafeCacheFile, }, proxy::AnyOutboundHandler, }; diff --git a/clash_lib/src/app/api/middlewares/auth.rs b/clash_lib/src/app/api/middlewares/auth.rs index c28fc8e1c..807d7ebf8 100644 --- a/clash_lib/src/app/api/middlewares/auth.rs +++ b/clash_lib/src/app/api/middlewares/auth.rs @@ -1,6 +1,4 @@ -use axum::extract::Query; -use axum::http::Request; -use axum::{body::Body, response::Response}; +use axum::{body::Body, extract::Query, http::Request, response::Response}; use futures::future::BoxFuture; use serde::Deserialize; @@ -54,11 +52,9 @@ where S: Service, Response = Response> + Send + 'static, S::Future: Send + 'static, { - type Response = S::Response; - type Error = S::Error; - type Future = BoxFuture<'static, Result>; + type Response = S::Response; fn poll_ready( &mut self, diff --git a/clash_lib/src/app/api/mod.rs b/clash_lib/src/app/api/mod.rs index e0b9253cb..8aa1db377 100644 --- a/clash_lib/src/app/api/mod.rs +++ b/clash_lib/src/app/api/mod.rs @@ -1,27 +1,24 @@ -use std::net::SocketAddr; -use std::path::PathBuf; -use std::sync::Arc; +use std::{net::SocketAddr, path::PathBuf, sync::Arc}; use axum::{response::Redirect, routing::get, Router}; -use http::header; -use http::Method; +use http::{header, Method}; use tokio::sync::{broadcast::Sender, Mutex}; use tower::ServiceBuilder; -use tower_http::cors::{Any, CorsLayer}; -use tower_http::services::ServeDir; -use tower_http::trace::TraceLayer; +use tower_http::{ + cors::{Any, CorsLayer}, + services::ServeDir, + trace::TraceLayer, +}; use tracing::{error, info}; use crate::{config::internal::config::Controller, GlobalState, Runner}; -use super::dispatcher::StatisticsManager; -use super::dns::ThreadSafeDNSResolver; -use super::logging::LogEvent; -use super::profile::ThreadSafeCacheFile; use super::{ - dispatcher, inbound::manager::ThreadSafeInboundManager, - outbound::manager::ThreadSafeOutboundManager, router::ThreadSafeRouter, + dispatcher, dispatcher::StatisticsManager, dns::ThreadSafeDNSResolver, + inbound::manager::ThreadSafeInboundManager, logging::LogEvent, + outbound::manager::ThreadSafeOutboundManager, profile::ThreadSafeCacheFile, + router::ThreadSafeRouter, }; mod handlers; @@ -104,7 +101,10 @@ pub fn get_api_runner( if let Some(external_ui) = controller_cfg.external_ui { app = app .route("/ui", get(|| async { Redirect::to("/ui/") })) - .nest_service("/ui/", ServeDir::new(PathBuf::from(cwd).join(external_ui))); + .nest_service( + "/ui/", + ServeDir::new(PathBuf::from(cwd).join(external_ui)), + ); } let listener = tokio::net::TcpListener::bind(&bind_addr).await.unwrap(); diff --git a/clash_lib/src/app/dispatcher/dispatcher_impl.rs b/clash_lib/src/app/dispatcher/dispatcher_impl.rs index 4456fc3b4..52cb517c1 100644 --- a/clash_lib/src/app/dispatcher/dispatcher_impl.rs +++ b/clash_lib/src/app/dispatcher/dispatcher_impl.rs @@ -1,31 +1,31 @@ -use crate::app::dispatcher::tracked::TrackedDatagram; -use crate::app::dispatcher::tracked::TrackedStream; -use crate::app::outbound::manager::ThreadSafeOutboundManager; -use crate::app::router::ThreadSafeRouter; -use crate::common::io::copy_buf_bidirectional_with_timeout; -use crate::config::def::RunMode; -use crate::config::internal::proxy::PROXY_DIRECT; -use crate::config::internal::proxy::PROXY_GLOBAL; -use crate::proxy::datagram::UdpPacket; -use crate::proxy::AnyInboundDatagram; -use crate::session::Session; -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::sync::Mutex; -use std::time::Duration; -use std::time::Instant; -use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use tokio::sync::RwLock; -use tokio::task::JoinHandle; -use tracing::info_span; -use tracing::instrument; -use tracing::trace; -use tracing::Instrument; -use tracing::{debug, error, info, warn}; +use crate::{ + app::{ + dispatcher::tracked::{TrackedDatagram, TrackedStream}, + outbound::manager::ThreadSafeOutboundManager, + router::ThreadSafeRouter, + }, + common::io::copy_buf_bidirectional_with_timeout, + config::{ + def::RunMode, + internal::proxy::{PROXY_DIRECT, PROXY_GLOBAL}, + }, + proxy::{datagram::UdpPacket, AnyInboundDatagram}, + session::Session, +}; +use futures::{SinkExt, StreamExt}; +use std::{ + collections::HashMap, + fmt::{Debug, Formatter}, + net::SocketAddr, + sync::{Arc, Mutex}, + time::{Duration, Instant}, +}; +use tokio::{ + io::{AsyncRead, AsyncWrite, AsyncWriteExt}, + sync::RwLock, + task::JoinHandle, +}; +use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument}; use crate::app::dns::ThreadSafeDNSResolver; @@ -88,8 +88,10 @@ impl Dispatcher { match host { Some(host) => { let mut sess = sess; - sess.destination = - crate::session::SocksAddr::Domain(host, addr.port()); + sess.destination = crate::session::SocksAddr::Domain( + host, + addr.port(), + ); sess } None => { @@ -101,7 +103,7 @@ impl Dispatcher { sess } } - crate::session::SocksAddr::Domain(_, _) => sess, + crate::session::SocksAddr::Domain(..) => sess, } } else { sess @@ -129,8 +131,13 @@ impl Dispatcher { { Ok(rhs) => { debug!("remote connection established {}", sess); - let mut rhs = - TrackedStream::new(rhs, self.manager.clone(), sess.clone(), rule).await; + let mut rhs = TrackedStream::new( + rhs, + self.manager.clone(), + sess.clone(), + rule, + ) + .await; match copy_buf_bidirectional_with_timeout( &mut lhs, &mut rhs, @@ -151,49 +158,60 @@ impl Dispatcher { ); } Err(err) => match err { - crate::common::io::CopyBidirectionalError::LeftClosed(err) => { - match err.kind() { - std::io::ErrorKind::UnexpectedEof - | std::io::ErrorKind::ConnectionReset - | std::io::ErrorKind::BrokenPipe => { - debug!( - "connection {} closed with error {} by local", - sess, err - ); - } - _ => { - warn!("connection {} closed with error {} by local", sess, err); - } + crate::common::io::CopyBidirectionalError::LeftClosed( + err, + ) => match err.kind() { + std::io::ErrorKind::UnexpectedEof + | std::io::ErrorKind::ConnectionReset + | std::io::ErrorKind::BrokenPipe => { + debug!( + "connection {} closed with error {} by local", + sess, err + ); } - } - crate::common::io::CopyBidirectionalError::RightClosed(err) => { + _ => { + warn!( + "connection {} closed with error {} by local", + sess, err + ); + } + }, + crate::common::io::CopyBidirectionalError::RightClosed( + err, + ) => match err.kind() { + std::io::ErrorKind::UnexpectedEof + | std::io::ErrorKind::ConnectionReset + | std::io::ErrorKind::BrokenPipe => { + debug!( + "connection {} closed with error {} by remote", + sess, err + ); + } + _ => { + warn!( + "connection {} closed with error {} by remote", + sess, err + ); + } + }, + crate::common::io::CopyBidirectionalError::Other(err) => { match err.kind() { std::io::ErrorKind::UnexpectedEof | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::BrokenPipe => { debug!( - "connection {} closed with error {} by remote", + "connection {} closed with error {}", sess, err ); } _ => { warn!( - "connection {} closed with error {} by remote", + "connection {} closed with error {}", sess, err ); } } } - crate::common::io::CopyBidirectionalError::Other(err) => match err.kind() { - std::io::ErrorKind::UnexpectedEof - | std::io::ErrorKind::ConnectionReset - | std::io::ErrorKind::BrokenPipe => { - debug!("connection {} closed with error {}", sess, err); - } - _ => { - warn!("connection {} closed with error {}", sess, err); - } - }, }, } } @@ -226,7 +244,8 @@ impl Dispatcher { let manager = self.manager.clone(); let (mut local_w, mut local_r) = udp_inbound.split(); - let (remote_receiver_w, mut remote_receiver_r) = tokio::sync::mpsc::channel(32); + let (remote_receiver_w, mut remote_receiver_r) = + tokio::sync::mpsc::channel(32); let s = sess.clone(); let ss = sess.clone(); @@ -250,11 +269,17 @@ impl Dispatcher { trace!("fake ip resolved to {}", host); let mut sess = sess; sess.destination = - crate::session::SocksAddr::Domain(host, addr.port()); + crate::session::SocksAddr::Domain( + host, + addr.port(), + ); sess } None => { - error!("failed to reverse lookup fake ip: {}", ip); + error!( + "failed to reverse lookup fake ip: {}", + ip + ); continue; } } @@ -262,7 +287,7 @@ impl Dispatcher { sess } } - crate::session::SocksAddr::Domain(_, _) => sess, + crate::session::SocksAddr::Domain(..) => sess, } } else { sess @@ -287,28 +312,38 @@ impl Dispatcher { let remote_receiver_w = remote_receiver_w.clone(); let mgr = outbound_manager.clone(); - let handler = mgr.get_outbound(&outbound_name).unwrap_or_else(|| { - debug!("unknown rule: {}, fallback to direct", outbound_name); - mgr.get_outbound(PROXY_DIRECT).unwrap() - }); + let handler = + mgr.get_outbound(&outbound_name).unwrap_or_else(|| { + debug!( + "unknown rule: {}, fallback to direct", + outbound_name + ); + mgr.get_outbound(PROXY_DIRECT).unwrap() + }); 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 + packet.src_addr.clone().must_into_socket_addr(), /* this is only + * expected to be + * socket addr as it's + * from local + * udp */ ) .await { None => { debug!("building {} outbound datagram connecting", sess); - let outbound_datagram = - match handler.connect_datagram(&sess, resolver.clone()).await { - Ok(v) => v, - Err(err) => { - error!("failed to connect outbound: {}", err); - continue; - } - }; + let outbound_datagram = match handler + .connect_datagram(&sess, resolver.clone()) + .await + { + Ok(v) => v, + Err(err) => { + error!("failed to connect outbound: {}", err); + continue; + } + }; debug!("{} outbound datagram connected", sess); @@ -332,11 +367,17 @@ impl Dispatcher { packet.src_addr = sess.destination.clone(); packet.dst_addr = sess.source.into(); - debug!("UDP NAT for packet: {:?}, session: {}", packet, sess); + debug!( + "UDP NAT for packet: {:?}, session: {}", + packet, sess + ); match remote_receiver_w.send(packet).await { Ok(_) => {} Err(err) => { - warn!("failed to send packet to local: {}", err); + warn!( + "failed to send packet to local: {}", + err + ); } } } @@ -347,7 +388,10 @@ impl Dispatcher { match remote_w.send(packet).await { Ok(_) => {} Err(err) => { - warn!("failed to send packet to remote: {}", err); + warn!( + "failed to send packet to remote: {}", + err + ); } } } @@ -534,16 +578,16 @@ impl OutboundHandleMap { outbound_name: &str, src_addr: SocketAddr, ) -> Option { - self.0 - .get_mut(&(outbound_name.to_owned(), src_addr)) - .map(|(_, _, sender, last)| { + 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() - }) + }, + ) } } @@ -553,7 +597,7 @@ impl Drop for OutboundHandleMap { "dropping inner outbound handle map that has {} sessions", self.0.len() ); - for (_, (recv_handle, send_handle, _, _)) in self.0.drain() { + for (_, (recv_handle, send_handle, ..)) in self.0.drain() { recv_handle.abort(); send_handle.abort(); } diff --git a/clash_lib/src/app/dispatcher/mod.rs b/clash_lib/src/app/dispatcher/mod.rs index cf7c3e491..23683b6c8 100644 --- a/clash_lib/src/app/dispatcher/mod.rs +++ b/clash_lib/src/app/dispatcher/mod.rs @@ -4,9 +4,7 @@ mod tracked; pub use dispatcher_impl::Dispatcher; pub use statistics_manager::Manager as StatisticsManager; -pub use tracked::BoxedChainedDatagram; -pub use tracked::BoxedChainedStream; -pub use tracked::ChainedDatagram; -pub use tracked::ChainedDatagramWrapper; -pub use tracked::ChainedStream; -pub use tracked::ChainedStreamWrapper; +pub use tracked::{ + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, +}; diff --git a/clash_lib/src/app/dispatcher/statistics_manager.rs b/clash_lib/src/app/dispatcher/statistics_manager.rs index 4914c90f6..fe027b3fb 100644 --- a/clash_lib/src/app/dispatcher/statistics_manager.rs +++ b/clash_lib/src/app/dispatcher/statistics_manager.rs @@ -138,7 +138,7 @@ impl Manager { .fetch_add(n as i64, std::sync::atomic::Ordering::Relaxed); } - //TODO: make this u64 + // TODO: make this u64 pub fn now(&self) -> (i64, i64) { ( self.upload_blip.load(std::sync::atomic::Ordering::Relaxed), @@ -156,7 +156,9 @@ impl Manager { connections.push(TrackerInfo { uuid: t.uuid, upload_total: AtomicU64::new(t.upload_total.load(Ordering::Acquire)), - download_total: AtomicU64::new(t.download_total.load(Ordering::Acquire)), + download_total: AtomicU64::new( + t.download_total.load(Ordering::Acquire), + ), start_time: t.start_time, proxy_chain: chain.clone(), rule: t.rule.clone(), @@ -170,7 +172,9 @@ impl Manager { download_total: self .download_total .load(std::sync::atomic::Ordering::Relaxed), - upload_total: self.upload_total.load(std::sync::atomic::Ordering::Relaxed), + upload_total: self + .upload_total + .load(std::sync::atomic::Ordering::Relaxed), connections, } } diff --git a/clash_lib/src/app/dispatcher/tracked.rs b/clash_lib/src/app/dispatcher/tracked.rs index 26235664a..72ae8d4c7 100644 --- a/clash_lib/src/app/dispatcher/tracked.rs +++ b/clash_lib/src/app/dispatcher/tracked.rs @@ -9,7 +9,9 @@ use tokio::{ }; use tracing::debug; -use crate::{app::router::RuleMatcher, proxy::datagram::UdpPacket, session::Session}; +use crate::{ + app::router::RuleMatcher, proxy::datagram::UdpPacket, session::Session, +}; use super::statistics_manager::{Manager, ProxyChain, TrackerInfo}; @@ -26,7 +28,9 @@ impl Tracked { } #[async_trait] -pub trait ChainedStream: AsyncRead + AsyncWrite + Unpin + Debug + Send + Sync { +pub trait ChainedStream: + AsyncRead + AsyncWrite + Unpin + Debug + Send + Sync +{ fn chain(&self) -> &ProxyChain; async fn append_to_chain(&self, name: &str); } @@ -138,7 +142,9 @@ impl TrackedStream { .as_ref() .map(|x| x.type_name().to_owned()) .unwrap_or_default(), - rule_payload: rule.map(|x| x.payload().to_owned()).unwrap_or_default(), + rule_payload: rule + .map(|x| x.payload().to_owned()) + .unwrap_or_default(), proxy_chain_holder: chain.clone(), ..Default::default() }), @@ -319,6 +325,7 @@ where T: Sink + Unpin, { type Error = std::io::Error; + fn poll_ready( self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -375,7 +382,9 @@ impl TrackedDatagram { .as_ref() .map(|x| x.type_name().to_owned()) .unwrap_or_default(), - rule_payload: rule.map(|x| x.payload().to_owned()).unwrap_or_default(), + rule_payload: rule + .map(|x| x.payload().to_owned()) + .unwrap_or_default(), proxy_chain_holder: chain.clone(), ..Default::default() }), @@ -421,9 +430,10 @@ impl Stream for TrackedDatagram { let r = Pin::new(self.inner.as_mut()).poll_next(cx); if let Poll::Ready(Some(ref pkt)) = r { self.manager.push_downloaded(pkt.data.len()); - self.tracker - .download_total - .fetch_add(pkt.data.len() as u64, std::sync::atomic::Ordering::Relaxed); + self.tracker.download_total.fetch_add( + pkt.data.len() as u64, + std::sync::atomic::Ordering::Relaxed, + ); } r } @@ -448,12 +458,17 @@ impl Sink for TrackedDatagram { Pin::new(self.inner.as_mut()).poll_ready(cx) } - fn start_send(mut self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + fn start_send( + mut self: Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { match self.close_notify.try_recv() { Ok(_) => return Err(std::io::ErrorKind::BrokenPipe.into()), Err(e) => match e { TryRecvError::Empty => {} - TryRecvError::Closed => return Err(std::io::ErrorKind::BrokenPipe.into()), + TryRecvError::Closed => { + return Err(std::io::ErrorKind::BrokenPipe.into()) + } }, } diff --git a/clash_lib/src/app/dns/config.rs b/clash_lib/src/app/dns/config.rs index 15e7bf7cb..84a96fdbd 100644 --- a/clash_lib/src/app/dns/config.rs +++ b/clash_lib/src/app/dns/config.rs @@ -95,7 +95,10 @@ impl Config { server = "udp://".to_owned() + &server; } let url = Url::parse(&server).map_err(|_x| { - Error::InvalidConfig(format!("invalid dns server: {}", server.as_str())) + Error::InvalidConfig(format!( + "invalid dns server: {}", + server.as_str() + )) })?; let host = url.host_str().expect("dns host must be valid"); @@ -164,7 +167,9 @@ impl Config { Ok(policy) } - pub fn parse_fallback_ip_cidr(ipcidr: &[String]) -> anyhow::Result> { + pub fn parse_fallback_ip_cidr( + ipcidr: &[String], + ) -> anyhow::Result> { let mut output = vec![]; for ip in ipcidr.iter() { @@ -226,7 +231,8 @@ impl TryFrom<&crate::config::def::Config> for Config { let nameservers = Config::parse_nameserver(&dc.nameserver)?; let fallback = Config::parse_nameserver(&dc.fallback)?; - let nameserver_policy = Config::parse_nameserver_policy(&dc.nameserver_policy)?; + let nameserver_policy = + Config::parse_nameserver_policy(&dc.nameserver_policy)?; if dc.default_nameserver.is_empty() { return Err(Error::InvalidConfig(String::from( @@ -253,7 +259,10 @@ impl TryFrom<&crate::config::def::Config> for Config { .map(|l| match l { DNSListen::Udp(u) => { let addr = u.parse::().map_err(|_| { - Error::InvalidConfig(format!("invalid dns udp listen address: {}", u)) + Error::InvalidConfig(format!( + "invalid dns udp listen address: {}", + u + )) })?; Ok(DNSListenAddr { udp: Some(addr), @@ -278,7 +287,9 @@ impl TryFrom<&crate::config::def::Config> for Config { "tcp" => tcp = Some(addr), "doh" => { let mut buf_read: Box = - Box::new(BufReader::new(TEST_CERT.as_bytes())); + Box::new(BufReader::new( + TEST_CERT.as_bytes(), + )); let certs = rustls_pemfile::certs(&mut buf_read) .unwrap() .into_iter() @@ -286,18 +297,30 @@ impl TryFrom<&crate::config::def::Config> for Config { .collect::>(); let mut buf_read: Box = - Box::new(BufReader::new(TEST_KEY.as_bytes())); + Box::new(BufReader::new( + TEST_KEY.as_bytes(), + )); let mut keys = - rustls_pemfile::pkcs8_private_keys(&mut buf_read).unwrap(); + rustls_pemfile::pkcs8_private_keys( + &mut buf_read, + ) + .unwrap(); let c = DoHConfig { - certificate_and_key: (certs, PrivateKey(keys.remove(0))), - dns_hostname: Some("dns.example.com".to_owned()), + certificate_and_key: ( + certs, + PrivateKey(keys.remove(0)), + ), + dns_hostname: Some( + "dns.example.com".to_owned(), + ), }; doh = Some((addr, c)) } "dot" => { let mut buf_read: Box = - Box::new(BufReader::new(TEST_CERT.as_bytes())); + Box::new(BufReader::new( + TEST_CERT.as_bytes(), + )); let certs = rustls_pemfile::certs(&mut buf_read) .unwrap() .into_iter() @@ -305,11 +328,19 @@ impl TryFrom<&crate::config::def::Config> for Config { .collect::>(); let mut buf_read: Box = - Box::new(BufReader::new(TEST_KEY.as_bytes())); + Box::new(BufReader::new( + TEST_KEY.as_bytes(), + )); let mut keys = - rustls_pemfile::pkcs8_private_keys(&mut buf_read).unwrap(); + rustls_pemfile::pkcs8_private_keys( + &mut buf_read, + ) + .unwrap(); let c = DoTConfig { - certificate_and_key: (certs, PrivateKey(keys.remove(0))), + certificate_and_key: ( + certs, + PrivateKey(keys.remove(0)), + ), }; dot = Some((addr, c)) } @@ -329,10 +360,9 @@ impl TryFrom<&crate::config::def::Config> for Config { .unwrap_or_default(), enhance_mode: dc.enhanced_mode.clone(), default_nameserver, - fake_ip_range: dc - .fake_ip_range - .parse::() - .map_err(|_| Error::InvalidConfig(String::from("invalid fake ip range")))?, + fake_ip_range: dc.fake_ip_range.parse::().map_err( + |_| Error::InvalidConfig(String::from("invalid fake ip range")), + )?, fake_ip_filter: dc.fake_ip_filter.clone(), store_fake_ip: c.profile.store_fake_ip, hosts: if dc.user_hosts && !c.hosts.is_empty() { diff --git a/clash_lib/src/app/dns/dhcp.rs b/clash_lib/src/app/dns/dhcp.rs index 50d08fd5f..37482c1f5 100644 --- a/clash_lib/src/app/dns/dhcp.rs +++ b/clash_lib/src/app/dns/dhcp.rs @@ -1,21 +1,25 @@ -use crate::dns::dns_client::DNSNetMode; -use crate::dns::helper::make_clients; -use crate::dns::{Client, Resolver, ThreadSafeDNSClient}; -use crate::proxy::utils::{new_udp_socket, Interface}; -use crate::{dns_debug, dns_warn}; +use crate::{ + dns::{ + dns_client::DNSNetMode, helper::make_clients, Client, Resolver, + ThreadSafeDNSClient, + }, + dns_debug, dns_warn, + proxy::utils::{new_udp_socket, Interface}, +}; use async_trait::async_trait; use dhcproto::{Decodable, Encodable}; use futures::FutureExt; use network_interface::{Addr, NetworkInterfaceConfig}; -use std::fmt::{Debug, Formatter}; -use std::net::Ipv4Addr; -use std::ops::Add; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::{env, io}; -use tokio::net::UdpSocket; -use tokio::sync::Mutex; -use tokio::task::yield_now; +use std::{ + env, + fmt::{Debug, Formatter}, + io, + net::Ipv4Addr, + ops::Add, + sync::Arc, + time::{Duration, Instant}, +}; +use tokio::{net::UdpSocket, sync::Mutex, task::yield_now}; use hickory_proto::op::Message; use tracing::{debug, warn}; @@ -60,7 +64,8 @@ impl Client for DhcpClient { dbg_str.push(format!("{:?}", c)); } debug!("using clients: {:?}", dbg_str); - tokio::time::timeout(DHCP_TIMEOUT, Resolver::batch_exchange(&clients, msg)).await? + tokio::time::timeout(DHCP_TIMEOUT, Resolver::batch_exchange(&clients, msg)) + .await? } } @@ -114,17 +119,21 @@ impl DhcpClient { inner.iface_expires_at = Instant::now().add(IFACE_TTL); let iface = network_interface::NetworkInterface::show() - .map_err(|x| io::Error::new(io::ErrorKind::Other, format!("list ifaces: {:?}", x)))? + .map_err(|x| { + io::Error::new(io::ErrorKind::Other, format!("list ifaces: {:?}", x)) + })? .into_iter() .find(|x| { - x.name == self.iface && x.addr.first().map(|x| x.ip().is_ipv4()).unwrap_or(false) + x.name == self.iface + && x.addr.first().map(|x| x.ip().is_ipv4()).unwrap_or(false) }) .ok_or(io::Error::new( io::ErrorKind::Other, format!("can not find interface: {}", self.iface), ))?; - // TODO: this API changed, need to check if .first() is expected. same to L103 + // TODO: this API changed, need to check if .first() is expected. same + // to L103 let addr = iface.addr.first().ok_or(io::Error::new( io::ErrorKind::Other, format!("no address on interface: {}", self.iface), @@ -145,10 +154,10 @@ impl DhcpClient { inner.dns_expires_at = Instant::now().add(DHCP_TTL); inner.iface_addr = ipnet::IpNet::new( v4.ip.into(), - u32::from( - v4.netmask - .ok_or(io::Error::new(io::ErrorKind::Other, "no netmask"))?, - ) + u32::from(v4.netmask.ok_or(io::Error::new( + io::ErrorKind::Other, + "no netmask", + ))?) .count_ones() as _, ) .map_err(|_x| { @@ -188,7 +197,9 @@ async fn probe_dns_server(iface: &str) -> io::Result> { let socket = listen_dhcp_client(iface).await?; let mac_address: Vec = network_interface::NetworkInterface::show() - .map_err(|_x| io::Error::new(io::ErrorKind::Other, format!("list ifaces: {:?}", iface)))? + .map_err(|_x| { + io::Error::new(io::ErrorKind::Other, format!("list ifaces: {:?}", iface)) + })? .into_iter() .find(|x| x.name == iface) .ok_or(io::Error::new( @@ -202,8 +213,9 @@ async fn probe_dns_server(iface: &str) -> io::Result> { ))? .split(':') .map(|x| { - u8::from_str_radix(x, 16) - .map_err(|_x| io::Error::new(io::ErrorKind::Other, "malformed MAC addr")) + u8::from_str_radix(x, 16).map_err(|_x| { + io::Error::new(io::ErrorKind::Other, "malformed MAC addr") + }) }) .collect::>>()?; @@ -242,16 +254,18 @@ async fn probe_dns_server(iface: &str) -> io::Result> { .expect("failed to receive DHCP offer"); // fucking deep if-else hell - if let Ok(reply) = dhcproto::v4::Message::from_bytes(&buf[..n_read]) { - if let Some(op) = reply.opts().get(dhcproto::v4::OptionCode::MessageType) { + if let Ok(reply) = dhcproto::v4::Message::from_bytes(&buf[..n_read]) + { + if let Some(op) = + reply.opts().get(dhcproto::v4::OptionCode::MessageType) + { match op { dhcproto::v4::DhcpOption::MessageType(msg_type) => { if msg_type == &dhcproto::v4::MessageType::Offer { if reply.xid() == xid { - if let Some(op) = reply - .opts() - .get(dhcproto::v4::OptionCode::DomainNameServer) - { + if let Some(op) = reply.opts().get( + dhcproto::v4::OptionCode::DomainNameServer, + ) { match op { dhcproto::v4::DhcpOption::DomainNameServer(dns) => { dns_debug!( diff --git a/clash_lib/src/app/dns/dns_client.rs b/clash_lib/src/app/dns/dns_client.rs index 92c86c819..f635d0a0d 100644 --- a/clash_lib/src/app/dns/dns_client.rs +++ b/clash_lib/src/app/dns/dns_client.rs @@ -1,35 +1,37 @@ -use std::fmt::{Debug, Display, Formatter}; -use std::net::SocketAddr; -use std::str::FromStr; -use std::{net, sync::Arc, time::Duration}; +use std::{ + fmt::{Debug, Display, Formatter}, + net, + net::SocketAddr, + str::FromStr, + sync::Arc, + time::Duration, +}; use async_trait::async_trait; -use hickory_client::client::AsyncClient; use hickory_client::{ - client, proto::iocompat::AsyncIoTokioAsStd, tcp::TcpClientStream, udp::UdpClientStream, + client, client::AsyncClient, proto::iocompat::AsyncIoTokioAsStd, + tcp::TcpClientStream, udp::UdpClientStream, }; use hickory_proto::error::ProtoError; use rustls::ClientConfig; -use tokio::sync::RwLock; -use tokio::task::JoinHandle; +use tokio::{sync::RwLock, task::JoinHandle}; use tracing::{info, warn}; -use crate::common::tls::{self, GLOBAL_ROOT_STORE}; -use crate::dns::dhcp::DhcpClient; -use crate::dns::ThreadSafeDNSClient; -use hickory_proto::h2::HttpsClientStreamBuilder; -use hickory_proto::op::Message; -use hickory_proto::rustls::tls_client_connect_with_bind_addr; +use crate::{ + common::tls::{self, GLOBAL_ROOT_STORE}, + dns::{dhcp::DhcpClient, ThreadSafeDNSClient}, +}; use hickory_proto::{ + h2::HttpsClientStreamBuilder, + op::Message, + rustls::tls_client_connect_with_bind_addr, xfer::{DnsRequest, DnsRequestOptions, FirstAnswer}, DnsHandle, }; -use tokio::net::TcpStream as TokioTcpStream; -use tokio::net::UdpSocket as TokioUdpSocket; +use tokio::net::{TcpStream as TokioTcpStream, UdpSocket as TokioUdpSocket}; -use crate::proxy::utils::Interface; -use crate::Error; +use crate::{proxy::utils::Interface, Error}; use super::{ClashResolver, Client}; @@ -146,10 +148,10 @@ impl DnsClient { other => { let ip = if let Some(r) = opts.r { - if let Some(ip) = r - .resolve(&opts.host, false) - .await - .map_err(|x| anyhow!("resolve hostname failure: {}", x.to_string()))? + if let Some(ip) = + r.resolve(&opts.host, false).await.map_err(|x| { + anyhow!("resolve hostname failure: {}", x.to_string()) + })? { ip } else { @@ -161,14 +163,19 @@ impl DnsClient { } } else { opts.host.parse::().map_err(|x| { - Error::DNSError(format!("resolve DNS hostname error: {}, {}", x, opts.host)) + Error::DNSError(format!( + "resolve DNS hostname error: {}, {}", + x, opts.host + )) })? }; match other { DNSNetMode::Udp => { - let cfg = - DnsConfig::Udp(net::SocketAddr::new(ip, opts.port), opts.iface.clone()); + let cfg = DnsConfig::Udp( + net::SocketAddr::new(ip, opts.port), + opts.iface.clone(), + ); Ok(Arc::new(Self { inner: Arc::new(RwLock::new(Inner { @@ -185,8 +192,10 @@ impl DnsClient { })) } DNSNetMode::Tcp => { - let cfg = - DnsConfig::Tcp(net::SocketAddr::new(ip, opts.port), opts.iface.clone()); + let cfg = DnsConfig::Tcp( + net::SocketAddr::new(ip, opts.port), + opts.iface.clone(), + ); Ok(Arc::new(Self { inner: Arc::new(RwLock::new(Inner { @@ -272,7 +281,10 @@ impl Client for DnsClient { if let Some(bg) = &inner.bg_handle { if bg.is_finished() { - warn!("dns client background task is finished, likely connection closed, restarting a new one"); + warn!( + "dns client background task is finished, likely connection \ + closed, restarting a new one" + ); let (client, bg) = dns_stream_builder(&self.cfg).await?; inner.c.replace(client); inner.bg_handle.replace(bg); @@ -305,30 +317,32 @@ async fn dns_stream_builder( ) -> Result<(AsyncClient, JoinHandle>), Error> { match cfg { DnsConfig::Udp(addr, iface) => { - let stream = UdpClientStream::::with_bind_addr_and_timeout( - net::SocketAddr::new(addr.ip(), addr.port()), - // TODO: simplify this match - match iface { - Some(Interface::IpAddr(ip)) => Some(SocketAddr::new(*ip, 0)), - _ => None, - }, - Duration::from_secs(5), - ); - client::AsyncClient::connect(stream) - .await - .map(|(x, y)| (x, tokio::spawn(y))) - .map_err(|x| Error::DNSError(x.to_string())) - } - DnsConfig::Tcp(addr, iface) => { - let (stream, sender) = - TcpClientStream::>::with_bind_addr_and_timeout( + let stream = + UdpClientStream::::with_bind_addr_and_timeout( net::SocketAddr::new(addr.ip(), addr.port()), + // TODO: simplify this match match iface { Some(Interface::IpAddr(ip)) => Some(SocketAddr::new(*ip, 0)), _ => None, }, Duration::from_secs(5), ); + client::AsyncClient::connect(stream) + .await + .map(|(x, y)| (x, tokio::spawn(y))) + .map_err(|x| Error::DNSError(x.to_string())) + } + DnsConfig::Tcp(addr, iface) => { + let (stream, sender) = TcpClientStream::< + AsyncIoTokioAsStd, + >::with_bind_addr_and_timeout( + net::SocketAddr::new(addr.ip(), addr.port()), + match iface { + Some(Interface::IpAddr(ip)) => Some(SocketAddr::new(*ip, 0)), + _ => None, + }, + Duration::from_secs(5), + ); client::AsyncClient::new(stream, sender, None) .await @@ -342,21 +356,27 @@ async fn dns_stream_builder( .with_no_client_auth(); tls_config.alpn_protocols = vec!["dot".into()]; - let (stream, sender) = - tls_client_connect_with_bind_addr::>( - net::SocketAddr::new(addr.ip(), addr.port()), - match iface { - Some(Interface::IpAddr(ip)) => Some(SocketAddr::new(*ip, 0)), - _ => None, - }, - host.clone(), - Arc::new(tls_config), - ); + let (stream, sender) = tls_client_connect_with_bind_addr::< + AsyncIoTokioAsStd, + >( + net::SocketAddr::new(addr.ip(), addr.port()), + match iface { + Some(Interface::IpAddr(ip)) => Some(SocketAddr::new(*ip, 0)), + _ => None, + }, + host.clone(), + Arc::new(tls_config), + ); - client::AsyncClient::with_timeout(stream, sender, Duration::from_secs(5), None) - .await - .map(|(x, y)| (x, tokio::spawn(y))) - .map_err(|x| Error::DNSError(x.to_string())) + client::AsyncClient::with_timeout( + stream, + sender, + Duration::from_secs(5), + None, + ) + .await + .map(|(x, y)| (x, tokio::spawn(y))) + .map_err(|x| Error::DNSError(x.to_string())) } DnsConfig::Https(addr, host, iface) => { let mut tls_config = ClientConfig::builder() diff --git a/clash_lib/src/app/dns/fakeip/file_store.rs b/clash_lib/src/app/dns/fakeip/file_store.rs index a4498bdc8..880075a37 100644 --- a/clash_lib/src/app/dns/fakeip/file_store.rs +++ b/clash_lib/src/app/dns/fakeip/file_store.rs @@ -43,6 +43,6 @@ impl Store for FileStore { } async fn copy_to(&self, #[allow(unused)] store: &mut Box) { - //NO-OP + // NO-OP } } diff --git a/clash_lib/src/app/dns/helper.rs b/clash_lib/src/app/dns/helper.rs index b6f790010..9b5b904e5 100644 --- a/clash_lib/src/app/dns/helper.rs +++ b/clash_lib/src/app/dns/helper.rs @@ -1,7 +1,11 @@ -use crate::dns::dns_client::{DNSNetMode, DnsClient, Opts}; -use crate::dns::{ClashResolver, ThreadSafeDNSClient}; -use crate::dns_debug; -use crate::proxy::utils::Interface; +use crate::{ + dns::{ + dns_client::{DNSNetMode, DnsClient, Opts}, + ClashResolver, ThreadSafeDNSClient, + }, + dns_debug, + proxy::utils::Interface, +}; use std::sync::Arc; use tracing::{debug, warn}; diff --git a/clash_lib/src/app/dns/mod.rs b/clash_lib/src/app/dns/mod.rs index 5f78bb49c..65d640bac 100644 --- a/clash_lib/src/app/dns/mod.rs +++ b/clash_lib/src/app/dns/mod.rs @@ -70,8 +70,11 @@ pub type ThreadSafeDNSResolver = Arc; #[cfg_attr(test, automock)] #[async_trait] pub trait ClashResolver: Sync + Send { - async fn resolve(&self, host: &str, enhanced: bool) - -> anyhow::Result>; + async fn resolve( + &self, + host: &str, + enhanced: bool, + ) -> anyhow::Result>; async fn resolve_v4( &self, host: &str, diff --git a/clash_lib/src/app/dns/resolver.rs b/clash_lib/src/app/dns/resolver.rs index edf81962b..00603c783 100644 --- a/clash_lib/src/app/dns/resolver.rs +++ b/clash_lib/src/app/dns/resolver.rs @@ -1,30 +1,36 @@ use async_trait::async_trait; use futures::{FutureExt, TryFutureExt}; use rand::prelude::SliceRandom; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::Relaxed; -use std::time::Duration; -use std::{net, sync::Arc}; +use std::{ + net, + sync::{ + atomic::{AtomicBool, Ordering::Relaxed}, + Arc, + }, + time::Duration, +}; use tokio::sync::RwLock; use tracing::{debug, instrument, warn}; use hickory_proto::{op, rr}; -use crate::app::profile::ThreadSafeCacheFile; -use crate::common::mmdb::Mmdb; -use crate::config::def::DNSMode; -use crate::dns::helper::make_clients; -use crate::dns::ThreadSafeDNSClient; -use crate::dns_debug; -use crate::{common::trie, Error}; +use crate::{ + app::profile::ThreadSafeCacheFile, + common::{mmdb::Mmdb, trie}, + config::def::DNSMode, + dns::{helper::make_clients, ThreadSafeDNSClient}, + dns_debug, Error, +}; -use super::fakeip::{self, FileStore, InMemStore, ThreadSafeFakeDns}; -use super::system::SystemResolver; use super::{ - filters::{DomainFilter, FallbackDomainFilter, FallbackIPFilter, GeoIPFilter, IPNetFilter}, - Config, + fakeip::{self, FileStore, InMemStore, ThreadSafeFakeDns}, + filters::{ + DomainFilter, FallbackDomainFilter, FallbackIPFilter, GeoIPFilter, + IPNetFilter, + }, + system::SystemResolver, + ClashResolver, Config, ResolverKind, ThreadSafeDNSResolver, }; -use super::{ClashResolver, ResolverKind, ThreadSafeDNSResolver}; static TTL: Duration = Duration::from_secs(60); @@ -79,7 +85,9 @@ impl Resolver { mmdb: Arc, ) -> ThreadSafeDNSResolver { if !cfg.enable { - return Arc::new(SystemResolver::new().expect("failed to create system resolver")); + return Arc::new( + SystemResolver::new().expect("failed to create system resolver"), + ); } let default_resolver = Arc::new(Resolver { @@ -97,10 +105,20 @@ impl Resolver { let r = Resolver { ipv6: AtomicBool::new(cfg.ipv6), - main: make_clients(cfg.nameserver.clone(), Some(default_resolver.clone())).await, + main: make_clients( + cfg.nameserver.clone(), + Some(default_resolver.clone()), + ) + .await, hosts: cfg.hosts.clone(), fallback: if !cfg.fallback.is_empty() { - Some(make_clients(cfg.fallback.clone(), Some(default_resolver.clone())).await) + Some( + make_clients( + cfg.fallback.clone(), + Some(default_resolver.clone()), + ) + .await, + ) } else { None }, @@ -120,15 +138,15 @@ impl Resolver { { let mut filters = vec![]; - filters.push( - Box::new(GeoIPFilter::new(&cfg.fallback_filter.geo_ip_code, mmdb)) - as Box, - ); + filters.push(Box::new(GeoIPFilter::new( + &cfg.fallback_filter.geo_ip_code, + mmdb, + )) as Box); if let Some(ipcidr) = &cfg.fallback_filter.ip_cidr { for subnet in ipcidr { - filters - .push(Box::new(IPNetFilter::new(*subnet)) as Box) + filters.push(Box::new(IPNetFilter::new(*subnet)) + as Box) } } @@ -137,7 +155,9 @@ impl Resolver { None }, lru_cache: Some(Arc::new(RwLock::new( - lru_time_cache::LruCache::with_expiry_duration_and_capacity(TTL, 4096), + lru_time_cache::LruCache::with_expiry_duration_and_capacity( + TTL, 4096, + ), ))), policy: if !cfg.nameserver_policy.is_empty() { let mut p = trie::StringTrie::new(); @@ -145,7 +165,11 @@ impl Resolver { p.insert( domain.as_str(), Arc::new( - make_clients(vec![ns.to_owned()], Some(default_resolver.clone())).await, + make_clients( + vec![ns.to_owned()], + Some(default_resolver.clone()), + ) + .await, ), ); } @@ -175,7 +199,9 @@ impl Resolver { .unwrap(), ))), DNSMode::RedirHost => { - warn!("dns redir-host is not supported and will not do anything"); + warn!( + "dns redir-host is not supported and will not do anything" + ); None } _ => None, @@ -195,7 +221,11 @@ impl Resolver { async move { c.exchange(message) .inspect_err(|x| { - debug!("DNS client {} resolve error: {}", c.id(), x.to_string()) + debug!( + "DNS client {} resolve error: {}", + c.id(), + x.to_string() + ) }) .await } @@ -256,7 +286,10 @@ impl Resolver { } } - async fn exchange_no_cache(&self, message: &op::Message) -> anyhow::Result { + async fn exchange_no_cache( + &self, + message: &op::Message, + ) -> anyhow::Result { let q = message.query().unwrap(); let query = async move { @@ -319,14 +352,21 @@ impl Resolver { None } - async fn ip_exchange(&self, message: &op::Message) -> anyhow::Result { + async fn ip_exchange( + &self, + message: &op::Message, + ) -> anyhow::Result { if let Some(matched) = self.match_policy(message) { return Resolver::batch_exchange(matched, message).await; } if self.should_only_query_fallback(message) { // self.fallback guaranteed in the above check - return Resolver::batch_exchange(self.fallback.as_ref().unwrap(), message).await; + return Resolver::batch_exchange( + self.fallback.as_ref().unwrap(), + message, + ) + .await; } let main_query = Resolver::batch_exchange(&self.main, message); @@ -335,7 +375,8 @@ impl Resolver { return main_query.await; } - let fallback_query = Resolver::batch_exchange(self.fallback.as_ref().unwrap(), message); + let fallback_query = + Resolver::batch_exchange(self.fallback.as_ref().unwrap(), message); if let Ok(main_result) = main_query.await { let ip_list = Resolver::ip_list_of_message(&main_result); @@ -379,7 +420,8 @@ impl Resolver { // helpers fn is_ip_request(q: &op::Query) -> bool { q.query_class() == rr::DNSClass::IN - && (q.query_type() == rr::RecordType::A || q.query_type() == rr::RecordType::AAAA) + && (q.query_type() == rr::RecordType::A + || q.query_type() == rr::RecordType::AAAA) } fn domain_name_of_message(m: &op::Message) -> Option { @@ -391,7 +433,8 @@ impl Resolver { m.answers() .iter() .filter(|r| { - r.record_type() == rr::RecordType::A || r.record_type() == rr::RecordType::AAAA + r.record_type() == rr::RecordType::A + || r.record_type() == rr::RecordType::AAAA }) .map(|r| match r.data() { Some(data) => match data { @@ -408,7 +451,11 @@ impl Resolver { #[async_trait] impl ClashResolver for Resolver { #[instrument(skip(self))] - async fn resolve(&self, host: &str, enhanced: bool) -> anyhow::Result> { + async fn resolve( + &self, + host: &str, + enhanced: bool, + ) -> anyhow::Result> { match self.ipv6.load(Relaxed) { true => { let fut1 = self @@ -432,6 +479,7 @@ impl ClashResolver for Resolver { .map(|ip| ip.map(net::IpAddr::from)), } } + async fn resolve_v4( &self, host: &str, @@ -558,14 +606,17 @@ impl ClashResolver for Resolver { #[cfg(test)] mod tests { - use crate::dns::dns_client::{DNSNetMode, DnsClient, Opts}; - use crate::dns::{Resolver, ThreadSafeDNSClient}; + use crate::dns::{ + dns_client::{DNSNetMode, DnsClient, Opts}, + Resolver, ThreadSafeDNSClient, + }; use hickory_client::{client, op}; - use hickory_proto::rr; - use hickory_proto::udp::UdpClientStream; - use hickory_proto::xfer::{DnsHandle, DnsRequest, DnsRequestOptions, FirstAnswer}; - use std::sync::Arc; - use std::time::Duration; + use hickory_proto::{ + rr, + udp::UdpClientStream, + xfer::{DnsHandle, DnsRequest, DnsRequestOptions, FirstAnswer}, + }; + use std::{sync::Arc, time::Duration}; use tokio::net::UdpSocket; #[tokio::test] diff --git a/clash_lib/src/app/dns/server/mod.rs b/clash_lib/src/app/dns/server/mod.rs index ae00e4986..a0e25fe7d 100644 --- a/clash_lib/src/app/dns/server/mod.rs +++ b/clash_lib/src/app/dns/server/mod.rs @@ -65,7 +65,8 @@ impl DnsHandler { let builder = MessageResponseBuilder::from_message_request(request); let mut header = Header::response_from_request(request.header()); - if request.query().query_type() == RecordType::AAAA && !self.resolver.ipv6() { + if request.query().query_type() == RecordType::AAAA && !self.resolver.ipv6() + { header.set_authoritative(true); let resp = builder.build_no_records(header); @@ -98,7 +99,8 @@ impl DnsHandler { rdata, )]; - let resp = builder.build(header, records.iter(), &[], &[], &[]); + let resp = + builder.build(header, records.iter(), &[], &[], &[]); return Ok(response_handle.send_response(resp).await?); } None => { @@ -137,8 +139,13 @@ impl DnsHandler { header.set_name_server_count(m.name_server_count()); header.set_additional_count(m.additional_count()); - let mut rv = - builder.build(header, m.answers(), m.name_servers(), &[], m.additionals()); + let mut rv = builder.build( + header, + m.answers(), + m.name_servers(), + &[], + m.additionals(), + ); if let Some(edns) = request.edns() { if edns.dnssec_ok() { @@ -193,7 +200,10 @@ impl RequestHandler for DnsHandler { static DEFAULT_DNS_SERVER_TIMEOUT: Duration = Duration::from_secs(5); -pub async fn get_dns_listener(cfg: Config, resolver: ThreadSafeDNSResolver) -> Option { +pub async fn get_dns_listener( + cfg: Config, + resolver: ThreadSafeDNSResolver, +) -> Option { if !cfg.enable { return None; } @@ -239,7 +249,11 @@ pub async fn get_dns_listener(cfg: Config, resolver: ThreadSafeDNSResolver) -> O .await .and_then(|x| { info!("dns server listening on dot: {}", c.0); - s.register_tls_listener(x, DEFAULT_DNS_SERVER_TIMEOUT, c.1.certificate_and_key)?; + s.register_tls_listener( + x, + DEFAULT_DNS_SERVER_TIMEOUT, + c.1.certificate_and_key, + )?; Ok(()) }) .ok()?; diff --git a/clash_lib/src/app/dns/system.rs b/clash_lib/src/app/dns/system.rs index f36869570..a0f35aa5d 100644 --- a/clash_lib/src/app/dns/system.rs +++ b/clash_lib/src/app/dns/system.rs @@ -6,17 +6,25 @@ use super::{ClashResolver, ResolverKind}; pub struct SystemResolver; -/// SystemResolver is a resolver that uses libc getaddrinfo to resolve hostnames. +/// SystemResolver is a resolver that uses libc getaddrinfo to resolve +/// hostnames. impl SystemResolver { pub fn new() -> anyhow::Result { - warn!("Default dns resolver doesn't support ipv6, please enable clash dns resolver if you need ipv6 support."); + warn!( + "Default dns resolver doesn't support ipv6, please enable clash dns \ + resolver if you need ipv6 support." + ); Ok(Self) } } #[async_trait] impl ClashResolver for SystemResolver { - async fn resolve(&self, host: &str, _: bool) -> anyhow::Result> { + async fn resolve( + &self, + host: &str, + _: bool, + ) -> anyhow::Result> { let response = tokio::net::lookup_host(format!("{}:0", host)) .await? .collect::>(); @@ -26,7 +34,11 @@ impl ClashResolver for SystemResolver { .choose(&mut rand::thread_rng())) } - async fn resolve_v4(&self, host: &str, _: bool) -> anyhow::Result> { + async fn resolve_v4( + &self, + host: &str, + _: bool, + ) -> anyhow::Result> { let response = tokio::net::lookup_host(format!("{}:0", host)) .await? .collect::>(); @@ -39,7 +51,12 @@ impl ClashResolver for SystemResolver { }) .choose(&mut rand::thread_rng())) } - async fn resolve_v6(&self, host: &str, _: bool) -> anyhow::Result> { + + async fn resolve_v6( + &self, + host: &str, + _: bool, + ) -> anyhow::Result> { let response = tokio::net::lookup_host(format!("{}:0", host)) .await? .collect::>(); @@ -102,7 +119,8 @@ mod tests { assert!(response.is_err()); assert_eq!( response.unwrap_err().to_string(), - "proto error: Label contains invalid characters: Err(Errors { invalid_mapping, disallowed_by_std3_ascii_rules })" + "proto error: Label contains invalid characters: Err(Errors { \ + invalid_mapping, disallowed_by_std3_ascii_rules })" ); } diff --git a/clash_lib/src/app/inbound/manager.rs b/clash_lib/src/app/inbound/manager.rs index c68670edf..de5491bd9 100644 --- a/clash_lib/src/app/inbound/manager.rs +++ b/clash_lib/src/app/inbound/manager.rs @@ -1,13 +1,16 @@ use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; -use crate::app::dispatcher::Dispatcher; -use crate::app::inbound::network_listener::{ListenerType, NetworkInboundListener}; -use crate::common::auth::ThreadSafeAuthenticator; -use crate::config::internal::config::{BindAddress, Inbound}; -use crate::{Error, Runner}; -use std::collections::HashMap; -use std::sync::Arc; +use crate::{ + app::{ + dispatcher::Dispatcher, + inbound::network_listener::{ListenerType, NetworkInboundListener}, + }, + common::auth::ThreadSafeAuthenticator, + config::internal::config::{BindAddress, Inbound}, + Error, Runner, +}; +use std::{collections::HashMap, sync::Arc}; pub struct InboundManager { network_listeners: HashMap, diff --git a/clash_lib/src/app/inbound/network_listener.rs b/clash_lib/src/app/inbound/network_listener.rs index 34642f769..ee0dafc9f 100644 --- a/clash_lib/src/app/inbound/network_listener.rs +++ b/clash_lib/src/app/inbound/network_listener.rs @@ -1,16 +1,18 @@ -use crate::common::auth::ThreadSafeAuthenticator; -use crate::config::internal::config::BindAddress; +use crate::{ + common::auth::ThreadSafeAuthenticator, config::internal::config::BindAddress, +}; use crate::proxy::{http, mixed, socks, AnyInboundListener}; -use crate::proxy::utils::Interface; -use crate::{Dispatcher, Error, Runner}; +use crate::{proxy::utils::Interface, Dispatcher, Error, Runner}; use futures::FutureExt; use network_interface::{Addr, NetworkInterfaceConfig}; use tracing::{info, warn}; -use std::net::{IpAddr, Ipv4Addr}; -use std::sync::Arc; +use std::{ + net::{IpAddr, Ipv4Addr}, + sync::Arc, +}; #[derive(Eq, PartialEq, Hash, Clone, Debug)] pub enum ListenerType { @@ -36,26 +38,27 @@ impl NetworkInboundListener { BindAddress::Any => { #[cfg(target_os = "ios")] { - let all_ifaces = - network_interface::NetworkInterface::show().expect("list interfaces"); + let all_ifaces = network_interface::NetworkInterface::show() + .expect("list interfaces"); for iface in all_ifaces.into_iter() { let ip = - iface - .addr - .map(|x| x.ip()) - .filter(|x| x.is_ipv4()) - .map(|x| match x { + iface.addr.map(|x| x.ip()).filter(|x| x.is_ipv4()).map( + |x| match x { IpAddr::V4(v4) => v4, IpAddr::V6(_) => unreachable!(), - }); + }, + ); if !ip.is_some() { continue; } let ip = ip.unwrap(); - if ip.is_unspecified() || ip.is_link_local() || ip.is_multicast() { + if ip.is_unspecified() + || ip.is_link_local() + || ip.is_multicast() + { continue; } @@ -70,7 +73,9 @@ impl NetworkInboundListener { } BindAddress::One(iface) => match iface { Interface::IpAddr(ip) => match ip { - IpAddr::V4(ip) => self.build_and_insert_listener(&mut runners, *ip), + IpAddr::V4(ip) => { + self.build_and_insert_listener(&mut runners, *ip) + } IpAddr::V6(_) => unreachable!("unsupported listening v6"), }, Interface::Name(iface) => { @@ -83,7 +88,11 @@ impl NetworkInboundListener { Addr::V4(v4) => v4.ip, Addr::V6(_) => unreachable!(), }) - .find(|x| !x.is_unspecified() && !x.is_link_local() && !x.is_multicast()) + .find(|x| { + !x.is_unspecified() + && !x.is_link_local() + && !x.is_multicast() + }) .expect("no valid ip"); self.build_and_insert_listener(&mut runners, ip); @@ -121,7 +130,10 @@ impl NetworkInboundListener { runners.push( async move { tcp_listener.listen_tcp().await.map_err(|e| { - warn!("handler of {:?} tcp listen failed: {}", listener_type, e); + warn!( + "handler of {:?} tcp listen failed: {}", + listener_type, e + ); e.into() }) } diff --git a/clash_lib/src/app/logging.rs b/clash_lib/src/app/logging.rs index aa24f38b4..13f2568d9 100644 --- a/clash_lib/src/app/logging.rs +++ b/clash_lib/src/app/logging.rs @@ -1,22 +1,15 @@ use std::io::IsTerminal; use crate::def::LogLevel; -use opentelemetry::global; -use opentelemetry::KeyValue; -use opentelemetry_sdk::trace; -use opentelemetry_sdk::Resource; +use opentelemetry::{global, KeyValue}; +use opentelemetry_sdk::{trace, Resource}; use serde::Serialize; use tokio::sync::broadcast::Sender; -use tracing::debug; -use tracing::error; -use tracing_appender::non_blocking::NonBlocking; -use tracing_appender::non_blocking::WorkerGuard; +use tracing::{debug, error}; +use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; use tracing_oslog::OsLogger; -use tracing_subscriber::filter::Directive; -use tracing_subscriber::prelude::*; -use tracing_subscriber::Layer; -use tracing_subscriber::{filter, EnvFilter}; +use tracing_subscriber::{filter, filter::Directive, prelude::*, EnvFilter, Layer}; impl From for filter::LevelFilter { fn from(level: LogLevel) -> Self { @@ -99,11 +92,15 @@ pub fn setup_logging( log_file: Option, ) -> anyhow::Result> { let filter = EnvFilter::builder() - .with_default_directive(format!("clash={}", level).parse::().unwrap()) + .with_default_directive( + format!("clash={}", level).parse::().unwrap(), + ) .from_env_lossy(); let jaeger = if std::env::var("JAEGER_ENABLED").is_ok() { - global::set_text_map_propagator(opentelemetry_jaeger_propagator::Propagator::new()); + global::set_text_map_propagator( + opentelemetry_jaeger_propagator::Propagator::new(), + ); global::set_error_handler(|e| { error!("OpenTelemetry error: {:?}", e); }) @@ -111,14 +108,13 @@ pub fn setup_logging( let otlp_exporter = opentelemetry_otlp::new_exporter().tonic(); - let tracer = - opentelemetry_otlp::new_pipeline() - .tracing() - .with_exporter(otlp_exporter) - .with_trace_config(trace::config().with_resource(Resource::new(vec![ - KeyValue::new("service.name", "clash-rs"), - ]))) - .install_batch(opentelemetry_sdk::runtime::Tokio)?; + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter(otlp_exporter) + .with_trace_config(trace::config().with_resource(Resource::new(vec![ + KeyValue::new("service.name", "clash-rs"), + ]))) + .install_batch(opentelemetry_sdk::runtime::Tokio)?; Some(tracing_opentelemetry::layer().with_tracer(tracer)) } else { @@ -159,7 +155,9 @@ pub fn setup_logging( .with_line_number(true) .with_level(true) .with_thread_ids(true) - .with_writer(move || -> Box { Box::new(W(appender.clone())) }) + .with_writer(move || -> Box { + Box::new(W(appender.clone())) + }) .with_writer(std::io::stdout), ) .with(ios_os_log); @@ -193,7 +191,11 @@ impl<'a> tracing::field::Visit for EventVisitor<'a> { println!("str {} = {}", field.name(), value); } - fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + fn record_debug( + &mut self, + field: &tracing::field::Field, + value: &dyn std::fmt::Debug, + ) { if field.name() == "message" { self.0.push(format!("{:?}", value)); } else { diff --git a/clash_lib/src/app/outbound/manager.rs b/clash_lib/src/app/outbound/manager.rs index 639eb436c..f59c024d7 100644 --- a/clash_lib/src/app/outbound/manager.rs +++ b/clash_lib/src/app/outbound/manager.rs @@ -1,39 +1,38 @@ use anyhow::Result; use erased_serde::Serialize; use hyper::Uri; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::Duration; +use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use tokio::sync::{Mutex, RwLock}; -use tracing::debug; -use tracing::error; -use tracing::warn; +use tracing::{debug, error, warn}; use tracing::info; -use crate::app::dns::ThreadSafeDNSResolver; -use crate::app::profile::ThreadSafeCacheFile; -use crate::app::remote_content_manager::healthcheck::HealthCheck; -use crate::app::remote_content_manager::providers::file_vehicle; -use crate::app::remote_content_manager::providers::http_vehicle; -use crate::app::remote_content_manager::ProxyManager; - -use crate::app::remote_content_manager::providers::proxy_provider::PlainProvider; -use crate::app::remote_content_manager::providers::proxy_provider::ProxySetProvider; -use crate::app::remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider; -use crate::config::internal::proxy::PROXY_GLOBAL; -use crate::config::internal::proxy::{OutboundProxyProviderDef, PROXY_DIRECT, PROXY_REJECT}; -use crate::proxy::fallback; -use crate::proxy::loadbalance; -use crate::proxy::selector; - -use crate::proxy::selector::ThreadSafeSelectorControl; -use crate::proxy::urltest; -use crate::proxy::{reject, relay}; +use crate::app::{ + dns::ThreadSafeDNSResolver, + profile::ThreadSafeCacheFile, + remote_content_manager::{ + healthcheck::HealthCheck, + providers::{file_vehicle, http_vehicle}, + ProxyManager, + }, +}; + +use crate::{ + app::remote_content_manager::providers::proxy_provider::{ + PlainProvider, ProxySetProvider, ThreadSafeProxyProvider, + }, + config::internal::proxy::{ + OutboundProxyProviderDef, PROXY_DIRECT, PROXY_GLOBAL, PROXY_REJECT, + }, + proxy::{fallback, loadbalance, selector}, +}; + use crate::{ config::internal::proxy::{OutboundGroupProtocol, OutboundProxyProtocol}, - proxy::{direct, AnyOutboundHandler}, + proxy::{ + direct, reject, relay, selector::ThreadSafeSelectorControl, urltest, + AnyOutboundHandler, + }, Error, }; @@ -108,7 +107,10 @@ impl OutboundManager { } // API handles start - pub fn get_selector_control(&self, name: &str) -> Option { + pub fn get_selector_control( + &self, + name: &str, + ) -> Option { self.selector_control.get(name).cloned() } @@ -188,11 +190,13 @@ impl OutboundManager { for outbound in outbounds.iter() { match outbound { OutboundProxyProtocol::Direct => { - handlers.insert(PROXY_DIRECT.to_string(), direct::Handler::new()); + handlers + .insert(PROXY_DIRECT.to_string(), direct::Handler::new()); } OutboundProxyProtocol::Reject => { - handlers.insert(PROXY_REJECT.to_string(), reject::Handler::new()); + handlers + .insert(PROXY_REJECT.to_string(), reject::Handler::new()); } OutboundProxyProtocol::Ss(s) => { @@ -249,7 +253,9 @@ impl OutboundManager { .map(|x| { handlers .get(x) - .ok_or_else(|| Error::InvalidConfig(format!("proxy {} not found", x))) + .ok_or_else(|| { + Error::InvalidConfig(format!("proxy {} not found", x)) + }) .cloned() }) .collect::, _>>()?; @@ -264,8 +270,9 @@ impl OutboundManager { .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; let pd = Arc::new(RwLock::new( - PlainProvider::new(name.to_owned(), proxies, hc) - .map_err(|x| Error::InvalidConfig(format!("invalid provider config: {}", x)))?, + PlainProvider::new(name.to_owned(), proxies, hc).map_err(|x| { + Error::InvalidConfig(format!("invalid provider config: {}", x)) + })?, )); proxy_providers.push(pd.clone()); @@ -309,7 +316,9 @@ impl OutboundManager { for provider_name in provider_names { let provider = provider_registry .get(provider_name) - .unwrap_or_else(|| panic!("provider {} not found", provider_name)) + .unwrap_or_else(|| { + panic!("provider {} not found", provider_name) + }) .clone(); providers.push(provider); } @@ -357,7 +366,9 @@ impl OutboundManager { for provider_name in provider_names { let provider = provider_registry .get(provider_name) - .unwrap_or_else(|| panic!("provider {} not found", provider_name)) + .unwrap_or_else(|| { + panic!("provider {} not found", provider_name) + }) .clone(); providers.push(provider); } @@ -408,7 +419,9 @@ impl OutboundManager { for provider_name in provider_names { let provider = provider_registry .get(provider_name) - .unwrap_or_else(|| panic!("provider {} not found", provider_name)) + .unwrap_or_else(|| { + panic!("provider {} not found", provider_name) + }) .clone(); providers.push(provider); } @@ -458,7 +471,9 @@ impl OutboundManager { for provider_name in provider_names { let provider = provider_registry .get(provider_name) - .unwrap_or_else(|| panic!("provider {} not found", provider_name)) + .unwrap_or_else(|| { + panic!("provider {} not found", provider_name) + }) .clone(); providers.push(provider); } @@ -507,14 +522,17 @@ impl OutboundManager { for provider_name in provider_names { let provider = provider_registry .get(provider_name) - .unwrap_or_else(|| panic!("provider {} not found", provider_name)) + .unwrap_or_else(|| { + panic!("provider {} not found", provider_name) + }) .clone(); providers.push(provider); } } - let stored_selection = cache_store.get_selected(&proto.name).await; + let stored_selection = + cache_store.get_selected(&proto.name).await; let selector = selector::Handler::new( selector::HandlerOptions { @@ -527,7 +545,8 @@ impl OutboundManager { .await; handlers.insert(proto.name.clone(), Arc::new(selector.clone())); - selector_control.insert(proto.name.clone(), Arc::new(Mutex::new(selector))); + selector_control + .insert(proto.name.clone(), Arc::new(Mutex::new(selector))); } } } @@ -578,9 +597,9 @@ impl OutboundManager { match provider { OutboundProxyProviderDef::Http(http) => { let vehicle = http_vehicle::Vehicle::new( - http.url - .parse::() - .unwrap_or_else(|_| panic!("invalid provider url: {}", http.url)), + http.url.parse::().unwrap_or_else(|_| { + panic!("invalid provider url: {}", http.url) + }), http.path, Some(cwd.clone()), resolver.clone(), @@ -592,14 +611,21 @@ impl OutboundManager { http.health_check.lazy.unwrap_or_default(), proxy_manager.clone(), ) - .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; + .map_err(|e| { + Error::InvalidConfig(format!("invalid hc config {}", e)) + })?; let provider = ProxySetProvider::new( name.clone(), Duration::from_secs(http.interval), Arc::new(vehicle), hc, ) - .map_err(|x| Error::InvalidConfig(format!("invalid provider config: {}", x)))?; + .map_err(|x| { + Error::InvalidConfig(format!( + "invalid provider config: {}", + x + )) + })?; provider_registry.insert(name, Arc::new(RwLock::new(provider))); } @@ -617,7 +643,9 @@ impl OutboundManager { file.health_check.lazy.unwrap_or_default(), proxy_manager.clone(), ) - .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; + .map_err(|e| { + Error::InvalidConfig(format!("invalid hc config {}", e)) + })?; let provider = ProxySetProvider::new( name.clone(), @@ -625,7 +653,12 @@ impl OutboundManager { Arc::new(vehicle), hc, ) - .map_err(|x| Error::InvalidConfig(format!("invalid provider config: {}", x)))?; + .map_err(|x| { + Error::InvalidConfig(format!( + "invalid provider config: {}", + x + )) + })?; provider_registry.insert(name, Arc::new(RwLock::new(provider))); } @@ -638,7 +671,11 @@ impl OutboundManager { match p.initialize().await { Ok(_) => {} Err(err) => { - error!("failed to initialize proxy provider {}: {}", p.name(), err); + error!( + "failed to initialize proxy provider {}: {}", + p.name(), + err + ); } } info!("initialized provider {}", p.name()); diff --git a/clash_lib/src/app/outbound/utils.rs b/clash_lib/src/app/outbound/utils.rs index bf8b6280f..a0c641501 100644 --- a/clash_lib/src/app/outbound/utils.rs +++ b/clash_lib/src/app/outbound/utils.rs @@ -6,7 +6,9 @@ use std::{ use crate::{config::internal::proxy::OutboundGroupProtocol, Error}; // copy paste from https://github.com/Dreamacro/clash/blob/6a661bff0c185f38c4bd9d21c91a3233ba5fdb97/config/utils.go#L21 -pub fn proxy_groups_dag_sort(groups: &mut [OutboundGroupProtocol]) -> Result<(), Error> { +pub fn proxy_groups_dag_sort( + groups: &mut [OutboundGroupProtocol], +) -> Result<(), Error> { struct Node { in_degree: i32, @@ -171,8 +173,8 @@ pub fn proxy_groups_dag_sort(groups: &mut [OutboundGroupProtocol]) -> Result<(), #[cfg(test)] mod tests { use crate::config::internal::proxy::{ - OutboundGroupFallback, OutboundGroupLoadBalance, OutboundGroupProtocol, OutboundGroupRelay, - OutboundGroupSelect, OutboundGroupUrlTest, + OutboundGroupFallback, OutboundGroupLoadBalance, OutboundGroupProtocol, + OutboundGroupRelay, OutboundGroupSelect, OutboundGroupUrlTest, }; #[test] diff --git a/clash_lib/src/app/profile/mod.rs b/clash_lib/src/app/profile/mod.rs index 7f1b4bf36..4dd2c7df7 100644 --- a/clash_lib/src/app/profile/mod.rs +++ b/clash_lib/src/app/profile/mod.rs @@ -107,7 +107,10 @@ impl CacheFile { Ok(s) => match serde_yaml::from_str(&s) { Ok(db) => db, Err(e) => { - error!("failed to parse cache file: {}, initilizing a new one", e); + error!( + "failed to parse cache file: {}, initilizing a new one", + e + ); Db { selected: HashMap::new(), ip_to_host: HashMap::new(), diff --git a/clash_lib/src/app/remote_content_manager/healthcheck.rs b/clash_lib/src/app/remote_content_manager/healthcheck.rs index 1665ced77..7fd485aff 100644 --- a/clash_lib/src/app/remote_content_manager/healthcheck.rs +++ b/clash_lib/src/app/remote_content_manager/healthcheck.rs @@ -61,7 +61,8 @@ impl HealthCheck { let proxy_manager = self.proxy_manager.clone(); let url = self.url.clone(); let task_handle = tokio::spawn(async move { - let mut ticker = tokio::time::interval(tokio::time::Duration::from_secs(interval)); + let mut ticker = + tokio::time::interval(tokio::time::Duration::from_secs(interval)); loop { tokio::select! { _ = ticker.tick() => { diff --git a/clash_lib/src/app/remote_content_manager/http_client.rs b/clash_lib/src/app/remote_content_manager/http_client.rs index 55d33de9f..b0ccf2793 100644 --- a/clash_lib/src/app/remote_content_manager/http_client.rs +++ b/clash_lib/src/app/remote_content_manager/http_client.rs @@ -19,9 +19,10 @@ use crate::{ pub struct LocalConnector(pub AnyOutboundHandler, pub ThreadSafeDNSResolver); impl Service for LocalConnector { - type Response = BoxedChainedStream; type Error = std::io::Error; - type Future = Pin> + Send>>; + type Future = + Pin> + Send>>; + type Response = BoxedChainedStream; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) diff --git a/clash_lib/src/app/remote_content_manager/mod.rs b/clash_lib/src/app/remote_content_manager/mod.rs index 286f42bf4..67656deba 100644 --- a/clash_lib/src/app/remote_content_manager/mod.rs +++ b/clash_lib/src/app/remote_content_manager/mod.rs @@ -48,7 +48,8 @@ pub struct ProxyManager { proxy_state: Arc>>, dns_resolver: ThreadSafeDNSResolver, - connector_map: Arc>>>, + connector_map: + Arc>>>, } impl ProxyManager { @@ -168,12 +169,18 @@ impl ProxyManager { let resp = TimedFuture::new(client.request(req), None); let delay: u16 = - match tokio::time::timeout(timeout.unwrap_or(default_timeout), resp).await { + match tokio::time::timeout(timeout.unwrap_or(default_timeout), resp) + .await + { Ok((res, delay)) => match res { Ok(res) => { - let delay = delay.as_millis().try_into().expect("delay is too large"); + let delay = delay + .as_millis() + .try_into() + .expect("delay is too large"); trace!( - "urltest for proxy {} with url {} returned response {} in {}ms", + "urltest for proxy {} with url {} returned \ + response {} in {}ms", &name, url, res.status(), @@ -182,11 +189,16 @@ impl ProxyManager { Ok(delay) } Err(e) => { - debug!("urltest for proxy {} with url {} failed: {}", &name, url, e); + debug!( + "urltest for proxy {} with url {} failed: {}", + &name, url, e + ); Err(new_io_error(format!("{}: {}", url, e).as_str())) } }, - Err(_) => Err(new_io_error(format!("timeout for {}", url).as_str())), + Err(_) => { + Err(new_io_error(format!("timeout for {}", url).as_str())) + } }?; let req2 = Request::get(url) @@ -196,16 +208,20 @@ impl ProxyManager { .unwrap(); let resp2 = TimedFuture::new(client.request(req2), None); - let mean_delay: u16 = - match tokio::time::timeout(timeout.unwrap_or(default_timeout), resp2).await { - Ok((res, delay2)) => match res { - Ok(_) => ((delay2.as_millis() + delay as u128) / 2) - .try_into() - .expect("delay is too large"), - Err(_) => 0, - }, + let mean_delay: u16 = match tokio::time::timeout( + timeout.unwrap_or(default_timeout), + resp2, + ) + .await + { + Ok((res, delay2)) => match res { + Ok(_) => ((delay2.as_millis() + delay as u128) / 2) + .try_into() + .expect("delay is too large"), Err(_) => 0, - }; + }, + Err(_) => 0, + }; Ok((delay, mean_delay)) }; @@ -239,7 +255,10 @@ mod tests { use futures::TryFutureExt; use crate::{ - app::{dispatcher::ChainedStreamWrapper, dns::MockClashResolver, remote_content_manager}, + app::{ + dispatcher::ChainedStreamWrapper, dns::MockClashResolver, + remote_content_manager, + }, config::internal::proxy::PROXY_DIRECT, proxy::{direct, mocks::MockDummyOutboundHandler}, }; @@ -247,12 +266,13 @@ mod tests { #[tokio::test] async fn test_proxy_manager_alive() { let mut mock_resolver = MockClashResolver::new(); - mock_resolver - .expect_resolve() - .returning(|_, _| Ok(Some(std::net::IpAddr::V4(Ipv4Addr::new(172, 217, 167, 67))))); + mock_resolver.expect_resolve().returning(|_, _| { + Ok(Some(std::net::IpAddr::V4(Ipv4Addr::new(172, 217, 167, 67)))) + }); mock_resolver.expect_ipv6().return_const(false); - let manager = remote_content_manager::ProxyManager::new(Arc::new(mock_resolver)); + let manager = + remote_content_manager::ProxyManager::new(Arc::new(mock_resolver)); let mock_handler = direct::Handler::new(); @@ -291,11 +311,12 @@ mod tests { #[tokio::test] async fn test_proxy_manager_timeout() { let mut mock_resolver = MockClashResolver::new(); - mock_resolver - .expect_resolve() - .returning(|_, _| Ok(Some(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))))); + mock_resolver.expect_resolve().returning(|_, _| { + Ok(Some(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))) + }); - let manager = remote_content_manager::ProxyManager::new(Arc::new(mock_resolver)); + let manager = + remote_content_manager::ProxyManager::new(Arc::new(mock_resolver)); let mut mock_handler = MockDummyOutboundHandler::new(); mock_handler diff --git a/clash_lib/src/app/remote_content_manager/providers/fetcher.rs b/clash_lib/src/app/remote_content_manager/providers/fetcher.rs index 2c97b0b23..8a1d549c5 100644 --- a/clash_lib/src/app/remote_content_manager/providers/fetcher.rs +++ b/clash_lib/src/app/remote_content_manager/providers/fetcher.rs @@ -58,6 +58,7 @@ where on_update: on_update.map(|f| Arc::new(Mutex::new(f))), } } + pub fn name(&self) -> &str { self.name.as_str() } @@ -185,7 +186,11 @@ where } } - async fn pull_loop(&self, immediately_update: bool, mut ticker: tokio::time::Interval) { + async fn pull_loop( + &self, + immediately_update: bool, + mut ticker: tokio::time::Interval, + ) { let inner = self.inner.clone(); let vehicle = self.vehicle.clone(); let parser = self.parser.clone(); @@ -203,7 +208,9 @@ where let on_update = on_update.clone(); let update = || async move { let (elm, same) = - match Fetcher::::update_inner(inner, vehicle, parser).await { + match Fetcher::::update_inner(inner, vehicle, parser) + .await + { Ok((elm, same)) => (elm, same), Err(e) => { warn!("{} update failed: {}", &name, e); @@ -243,7 +250,9 @@ mod tests { use futures::future::BoxFuture; use tokio::time::sleep; - use crate::app::remote_content_manager::providers::{MockProviderVehicle, ProviderVehicleType}; + use crate::app::remote_content_manager::providers::{ + MockProviderVehicle, ProviderVehicleType, + }; use super::Fetcher; diff --git a/clash_lib/src/app/remote_content_manager/providers/http_vehicle.rs b/clash_lib/src/app/remote_content_manager/providers/http_vehicle.rs index eff895f8e..01a96894d 100644 --- a/clash_lib/src/app/remote_content_manager/providers/http_vehicle.rs +++ b/clash_lib/src/app/remote_content_manager/providers/http_vehicle.rs @@ -1,7 +1,11 @@ use super::{ProviderVehicle, ProviderVehicleType}; -use crate::app::dns::ThreadSafeDNSResolver; -use crate::common::errors::map_io_error; -use crate::common::http::{new_http_client, HttpClient}; +use crate::{ + app::dns::ThreadSafeDNSResolver, + common::{ + errors::map_io_error, + http::{new_http_client, HttpClient}, + }, +}; use async_trait::async_trait; @@ -24,7 +28,8 @@ impl Vehicle { cwd: Option

, dns_resolver: ThreadSafeDNSResolver, ) -> Self { - let client = new_http_client(dns_resolver).expect("failed to create http client"); + let client = + new_http_client(dns_resolver).expect("failed to create http client"); Self { url: url.into(), path: match cwd { @@ -62,8 +67,7 @@ impl ProviderVehicle for Vehicle { #[cfg(test)] mod tests { use super::ProviderVehicle; - use std::str; - use std::sync::Arc; + use std::{str, sync::Arc}; use hyper::Uri; diff --git a/clash_lib/src/app/remote_content_manager/providers/mod.rs b/clash_lib/src/app/remote_content_manager/providers/mod.rs index c2535b6ea..80323f5df 100644 --- a/clash_lib/src/app/remote_content_manager/providers/mod.rs +++ b/clash_lib/src/app/remote_content_manager/providers/mod.rs @@ -1,10 +1,12 @@ use async_trait::async_trait; use erased_serde::Serialize; use serde::Deserialize; -use std::collections::HashMap; -use std::fmt::{Display, Formatter}; -use std::io; -use std::sync::Arc; +use std::{ + collections::HashMap, + fmt::{Display, Formatter}, + io, + sync::Arc, +}; pub mod fetcher; pub mod file_vehicle; diff --git a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/mod.rs b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/mod.rs index 256ba13b6..68d61c290 100644 --- a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/mod.rs +++ b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/mod.rs @@ -10,7 +10,9 @@ use std::sync::Arc; use async_trait::async_trait; use tokio::sync::RwLock; -use crate::{app::remote_content_manager::providers::Provider, proxy::AnyOutboundHandler}; +use crate::{ + app::remote_content_manager::providers::Provider, proxy::AnyOutboundHandler, +}; pub type ThreadSafeProxyProvider = Arc>; diff --git a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/plain_provider.rs b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/plain_provider.rs index 5d2f45f1c..f6cd61283 100644 --- a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/plain_provider.rs +++ b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/plain_provider.rs @@ -30,7 +30,9 @@ impl PlainProvider { let hc = Arc::new(hc); if proxies.is_empty() { - return Err(Error::InvalidConfig(format!("{}: proxies is empty", name)).into()); + return Err( + Error::InvalidConfig(format!("{}: proxies is empty", name)).into() + ); } if hc.auto() { @@ -50,15 +52,19 @@ impl Provider for PlainProvider { fn name(&self) -> &str { &self.name } + fn vehicle_type(&self) -> ProviderVehicleType { ProviderVehicleType::Compatible } + fn typ(&self) -> ProviderType { ProviderType::Proxy } + async fn initialize(&self) -> std::io::Result<()> { Ok(()) } + async fn update(&self) -> std::io::Result<()> { Ok(()) } diff --git a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs index dfa026140..b80df2133 100644 --- a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs +++ b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs @@ -11,8 +11,10 @@ use super::ProxyProvider; use crate::{ app::remote_content_manager::{ healthcheck::HealthCheck, - providers::{fetcher::Fetcher, ThreadSafeProviderVehicle}, - providers::{Provider, ProviderType, ProviderVehicleType}, + providers::{ + fetcher::Fetcher, Provider, ProviderType, ProviderVehicleType, + ThreadSafeProviderVehicle, + }, }, common::errors::map_io_error, config::internal::proxy::OutboundProxyProtocol, @@ -31,10 +33,15 @@ struct Inner { hc: Arc, } -type ProxyUpdater = - Box) -> BoxFuture<'static, ()> + Send + Sync + 'static>; -type ProxyParser = - Box anyhow::Result> + Send + Sync + 'static>; +type ProxyUpdater = Box< + dyn Fn(Vec) -> BoxFuture<'static, ()> + + Send + + Sync + + 'static, +>; +type ProxyParser = Box< + dyn Fn(&[u8]) -> anyhow::Result> + Send + Sync + 'static, +>; pub struct ProxySetProvider { fetcher: Fetcher, @@ -87,19 +94,29 @@ impl ProxySetProvider { let n = name.clone(); let parser: ProxyParser = Box::new( move |input: &[u8]| -> anyhow::Result> { - let scheme: ProviderScheme = serde_yaml::from_slice(input).map_err(|x| { - Error::InvalidConfig(format!("proxy provider parse error {}: {}", n, x)) - })?; + let scheme: ProviderScheme = + serde_yaml::from_slice(input).map_err(|x| { + Error::InvalidConfig(format!( + "proxy provider parse error {}: {}", + n, x + )) + })?; let proxies = scheme.proxies; if let Some(proxies) = proxies { let proxies = proxies .into_iter() .filter_map(|x| OutboundProxyProtocol::try_from(x).ok()) .map(|x| match x { - OutboundProxyProtocol::Direct => Ok(direct::Handler::new()), - OutboundProxyProtocol::Reject => Ok(reject::Handler::new()), + OutboundProxyProtocol::Direct => { + Ok(direct::Handler::new()) + } + OutboundProxyProtocol::Reject => { + Ok(reject::Handler::new()) + } OutboundProxyProtocol::Ss(s) => s.try_into(), - OutboundProxyProtocol::Socks5(_) => todo!("socks5 not supported yet"), + OutboundProxyProtocol::Socks5(_) => { + todo!("socks5 not supported yet") + } OutboundProxyProtocol::Trojan(tr) => tr.try_into(), OutboundProxyProtocol::Vmess(vm) => vm.try_into(), OutboundProxyProtocol::Wireguard(wg) => wg.try_into(), @@ -109,7 +126,8 @@ impl ProxySetProvider { .collect::, _>>(); Ok(proxies?) } else { - Err(Error::InvalidConfig(format!("{}: proxies is empty", n)).into()) + Err(Error::InvalidConfig(format!("{}: proxies is empty", n)) + .into()) } }, ); @@ -204,7 +222,9 @@ mod tests { remote_content_manager::{ healthcheck::HealthCheck, providers::{ - proxy_provider::{proxy_set_provider::ProxySetProvider, ProxyProvider}, + proxy_provider::{ + proxy_set_provider::ProxySetProvider, ProxyProvider, + }, MockProviderVehicle, Provider, ProviderVehicleType, }, ProxyManager, @@ -250,8 +270,13 @@ proxies: ) .unwrap(); - let provider = - ProxySetProvider::new("test".to_owned(), Duration::from_secs(1), vehicle, hc).unwrap(); + let provider = ProxySetProvider::new( + "test".to_owned(), + Duration::from_secs(1), + vehicle, + hc, + ) + .unwrap(); assert_eq!(provider.proxies().await.len(), 0); diff --git a/clash_lib/src/app/remote_content_manager/providers/rule_provider/mod.rs b/clash_lib/src/app/remote_content_manager/providers/rule_provider/mod.rs index 1cec3bbdf..1f1c9b897 100644 --- a/clash_lib/src/app/remote_content_manager/providers/rule_provider/mod.rs +++ b/clash_lib/src/app/remote_content_manager/providers/rule_provider/mod.rs @@ -1,5 +1,4 @@ mod cidr_trie; mod provider; -pub use provider::ThreadSafeRuleProvider; -pub use provider::{RuleProviderImpl, RuleSetBehavior}; +pub use provider::{RuleProviderImpl, RuleSetBehavior, ThreadSafeRuleProvider}; diff --git a/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs b/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs index 7472069c9..ca69f6b9d 100644 --- a/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs +++ b/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs @@ -68,8 +68,10 @@ pub trait RuleProvider: Provider { pub type ThreadSafeRuleProvider = Arc; -type RuleUpdater = Box BoxFuture<'static, ()> + Send + Sync + 'static>; -type RuleParser = Box anyhow::Result + Send + Sync + 'static>; +type RuleUpdater = + Box BoxFuture<'static, ()> + Send + Sync + 'static>; +type RuleParser = + Box anyhow::Result + Send + Sync + 'static>; pub struct RuleProviderImpl { fetcher: Fetcher, @@ -87,8 +89,12 @@ impl RuleProviderImpl { ) -> Self { let inner = Arc::new(tokio::sync::RwLock::new(Inner { content: match behovior { - RuleSetBehavior::Domain => RuleContent::Domain(trie::StringTrie::new()), - RuleSetBehavior::Ipcidr => RuleContent::Ipcidr(Box::new(CidrTrie::new())), + RuleSetBehavior::Domain => { + RuleContent::Domain(trie::StringTrie::new()) + } + RuleSetBehavior::Ipcidr => { + RuleContent::Ipcidr(Box::new(CidrTrie::new())) + } RuleSetBehavior::Classical => RuleContent::Classical(vec![]), }, })); @@ -96,24 +102,30 @@ impl RuleProviderImpl { let inner_clone = inner.clone(); let n = name.clone(); - let updater: RuleUpdater = Box::new(move |input: RuleContent| -> BoxFuture<'static, ()> { - let n = n.clone(); - let inner: Arc> = inner_clone.clone(); - Box::pin(async move { - let mut inner = inner.write().await; - trace!("updated rules for: {}", n); - inner.content = input; - }) - }); + let updater: RuleUpdater = + Box::new(move |input: RuleContent| -> BoxFuture<'static, ()> { + let n = n.clone(); + let inner: Arc> = inner_clone.clone(); + Box::pin(async move { + let mut inner = inner.write().await; + trace!("updated rules for: {}", n); + inner.content = input; + }) + }); let n = name.clone(); - let parser: RuleParser = Box::new(move |input: &[u8]| -> anyhow::Result { - let scheme: ProviderScheme = serde_yaml::from_slice(input).map_err(|x| { - Error::InvalidConfig(format!("proxy provider parse error {}: {}", n, x)) - })?; - let rules = make_rules(behovior, scheme.payload, mmdb.clone())?; - Ok(rules) - }); + let parser: RuleParser = + Box::new(move |input: &[u8]| -> anyhow::Result { + let scheme: ProviderScheme = + serde_yaml::from_slice(input).map_err(|x| { + Error::InvalidConfig(format!( + "proxy provider parse error {}: {}", + n, x + )) + })?; + let rules = make_rules(behovior, scheme.payload, mmdb.clone())?; + Ok(rules) + }); let fetcher = Fetcher::new(name, interval, vehicle, parser, Some(updater)); @@ -132,7 +144,9 @@ impl RuleProvider for RuleProviderImpl { match inner { Ok(inner) => match &inner.content { - RuleContent::Domain(trie) => trie.search(&sess.destination.host()).is_some(), + RuleContent::Domain(trie) => { + trie.search(&sess.destination.host()).is_some() + } RuleContent::Ipcidr(trie) => trie.contains( sess.destination .ip() @@ -153,6 +167,7 @@ impl RuleProvider for RuleProviderImpl { } } } + fn behavior(&self) -> RuleSetBehavior { self.behavior } @@ -163,12 +178,15 @@ impl Provider for RuleProviderImpl { fn name(&self) -> &str { self.fetcher.name() } + fn vehicle_type(&self) -> ProviderVehicleType { self.fetcher.vehicle_type() } + fn typ(&self) -> ProviderType { ProviderType::Rule } + async fn initialize(&self) -> std::io::Result<()> { let ele = self.fetcher.initial().await.map_err(map_io_error)?; debug!("initializing rule provider {}", self.name()); @@ -177,6 +195,7 @@ impl Provider for RuleProviderImpl { } Ok(()) } + async fn update(&self) -> std::io::Result<()> { let (ele, same) = self.fetcher.update().await.map_err(map_io_error)?; debug!("rule provider {} updated. same? {}", self.name(), same); @@ -216,8 +235,12 @@ fn make_rules( mmdb: Arc, ) -> Result { match behavior { - RuleSetBehavior::Domain => Ok(RuleContent::Domain(make_domain_rules(rules)?)), - RuleSetBehavior::Ipcidr => Ok(RuleContent::Ipcidr(Box::new(make_ip_cidr_rules(rules)?))), + RuleSetBehavior::Domain => { + Ok(RuleContent::Domain(make_domain_rules(rules)?)) + } + RuleSetBehavior::Ipcidr => { + Ok(RuleContent::Ipcidr(Box::new(make_ip_cidr_rules(rules)?))) + } RuleSetBehavior::Classical => { Ok(RuleContent::Classical(make_classical_rules(rules, mmdb)?)) } @@ -248,9 +271,9 @@ fn make_classical_rules( for rule in rules { let parts = rule.split(',').map(str::trim).collect::>(); - // the rule inside RULE-SET is slightly different from the rule in config - // the target is always empty as it's holded in the RULE-SET container - // let's parse it manually + // the rule inside RULE-SET is slightly different from the rule in + // config the target is always empty as it's holded in the + // RULE-SET container let's parse it manually let rule_type = match parts.as_slice() { [proto, payload] => RuleType::new(proto, payload, "", None), [proto, payload, params @ ..] => { diff --git a/clash_lib/src/app/router/mod.rs b/clash_lib/src/app/router/mod.rs index 9fe89a8a7..af29e3d97 100644 --- a/clash_lib/src/app/router/mod.rs +++ b/clash_lib/src/app/router/mod.rs @@ -1,29 +1,30 @@ -use crate::app::router::rules::domain::Domain; -use crate::app::router::rules::domain_keyword::DomainKeyword; -use crate::app::router::rules::domain_suffix::DomainSuffix; -use crate::app::router::rules::ipcidr::IpCidr; -use crate::app::router::rules::ruleset::RuleSet; -use crate::Error; +use crate::{ + app::router::rules::{ + domain::Domain, domain_keyword::DomainKeyword, domain_suffix::DomainSuffix, + ipcidr::IpCidr, ruleset::RuleSet, + }, + Error, +}; -use crate::common::mmdb::Mmdb; -use crate::config::internal::config::RuleProviderDef; -use crate::config::internal::rule::RuleType; -use crate::session::{Session, SocksAddr}; +use crate::{ + common::mmdb::Mmdb, + config::internal::{config::RuleProviderDef, rule::RuleType}, + session::{Session, SocksAddr}, +}; use crate::app::router::rules::final_::Final; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::Duration; +use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use hyper::Uri; use tracing::{debug, error, info}; -use super::dns::ThreadSafeDNSResolver; -use super::remote_content_manager::providers::rule_provider::{ - RuleProviderImpl, ThreadSafeRuleProvider, +use super::{ + dns::ThreadSafeDNSResolver, + remote_content_manager::providers::{ + file_vehicle, http_vehicle, + rule_provider::{RuleProviderImpl, ThreadSafeRuleProvider}, + }, }; -use super::remote_content_manager::providers::{file_vehicle, http_vehicle}; mod rules; pub use rules::RuleMatcher; @@ -62,7 +63,9 @@ impl Router { Self { rules: rules .into_iter() - .map(|r| map_rule_type(r, mmdb.clone(), Some(&rule_provider_registry))) + .map(|r| { + map_rule_type(r, mmdb.clone(), Some(&rule_provider_registry)) + }) .collect(), dns_resolver, rule_provider_registry, @@ -77,7 +80,10 @@ impl Router { let mut sess_dup = sess.clone(); for r in self.rules.iter() { - if sess.destination.is_domain() && r.should_resolve_ip() && !sess_resolved { + if sess.destination.is_domain() + && r.should_resolve_ip() + && !sess_resolved + { debug!( "rule `{r}` resolving domain {} locally", sess.destination.domain().unwrap() @@ -87,7 +93,8 @@ impl Router { .resolve(sess.destination.domain().unwrap(), false) .await { - sess_dup.destination = SocksAddr::from((ip, sess.destination.port())); + sess_dup.destination = + SocksAddr::from((ip, sess.destination.port())); sess_resolved = true; } } @@ -117,9 +124,9 @@ impl Router { match provider { RuleProviderDef::Http(http) => { let vehicle = http_vehicle::Vehicle::new( - http.url - .parse::() - .unwrap_or_else(|_| panic!("invalid provider url: {}", http.url)), + http.url.parse::().unwrap_or_else(|_| { + panic!("invalid provider url: {}", http.url) + }), http.path, Some(cwd.clone()), resolver.clone(), @@ -165,7 +172,11 @@ impl Router { info!("rule provider {} initialized", p.name()); } Err(err) => { - error!("failed to initialize rule provider {}: {}", p.name(), err); + error!( + "failed to initialize rule provider {}: {}", + p.name(), + err + ); } } }); @@ -266,10 +277,14 @@ pub fn map_rule_type( target, rule_provider_registry .get(&rule_set) - .unwrap_or_else(|| panic!("rule provider {} not found", rule_set)) + .unwrap_or_else(|| { + panic!("rule provider {} not found", rule_set) + }) .clone(), )), - None => unreachable!("you shouldn't next rule-set within another rule-set"), + None => { + unreachable!("you shouldn't next rule-set within another rule-set") + } }, RuleType::Match { target } => Box::new(Final { target }), } diff --git a/clash_lib/src/app/router/rules/domain_suffix.rs b/clash_lib/src/app/router/rules/domain_suffix.rs index 5144c8cd8..a143da6cf 100644 --- a/clash_lib/src/app/router/rules/domain_suffix.rs +++ b/clash_lib/src/app/router/rules/domain_suffix.rs @@ -1,5 +1,7 @@ -use crate::app::router::rules::RuleMatcher; -use crate::session::{Session, SocksAddr}; +use crate::{ + app::router::rules::RuleMatcher, + session::{Session, SocksAddr}, +}; #[derive(Clone)] pub struct DomainSuffix { diff --git a/clash_lib/src/app/router/rules/final_.rs b/clash_lib/src/app/router/rules/final_.rs index ff0e14319..1d0c82b1f 100644 --- a/clash_lib/src/app/router/rules/final_.rs +++ b/clash_lib/src/app/router/rules/final_.rs @@ -1,5 +1,4 @@ -use crate::app::router::rules::RuleMatcher; -use crate::session::Session; +use crate::{app::router::rules::RuleMatcher, session::Session}; #[derive(Clone)] pub struct Final { diff --git a/clash_lib/src/app/router/rules/geoip.rs b/clash_lib/src/app/router/rules/geoip.rs index 86752ece9..63ea42782 100644 --- a/clash_lib/src/app/router/rules/geoip.rs +++ b/clash_lib/src/app/router/rules/geoip.rs @@ -23,7 +23,8 @@ impl std::fmt::Display for GeoIP { impl RuleMatcher for GeoIP { fn apply(&self, sess: &Session) -> bool { match sess.destination { - crate::session::SocksAddr::Ip(addr) => match self.mmdb.lookup(addr.ip()) { + crate::session::SocksAddr::Ip(addr) => match self.mmdb.lookup(addr.ip()) + { Ok(country) => { country .country @@ -37,9 +38,10 @@ impl RuleMatcher for GeoIP { false } }, - crate::session::SocksAddr::Domain(_, _) => false, + crate::session::SocksAddr::Domain(..) => false, } } + fn target(&self) -> &str { self.target.as_str() } diff --git a/clash_lib/src/app/router/rules/ipcidr.rs b/clash_lib/src/app/router/rules/ipcidr.rs index dfb5bd974..179e2eaf8 100644 --- a/clash_lib/src/app/router/rules/ipcidr.rs +++ b/clash_lib/src/app/router/rules/ipcidr.rs @@ -1,5 +1,7 @@ -use crate::app::router::rules::RuleMatcher; -use crate::session::{Session, SocksAddr}; +use crate::{ + app::router::rules::RuleMatcher, + session::{Session, SocksAddr}, +}; #[derive(Clone)] pub struct IpCidr { @@ -27,10 +29,11 @@ impl RuleMatcher for IpCidr { true => self.ipnet.contains(&sess.source.ip()), false => match &sess.destination { SocksAddr::Ip(ip) => self.ipnet.contains(&ip.ip()), - SocksAddr::Domain(_, _) => false, + SocksAddr::Domain(..) => false, }, } } + fn target(&self) -> &str { self.target.as_str() } diff --git a/clash_lib/src/app/router/rules/port.rs b/clash_lib/src/app/router/rules/port.rs index 981a43080..49e1385d2 100644 --- a/clash_lib/src/app/router/rules/port.rs +++ b/clash_lib/src/app/router/rules/port.rs @@ -1,5 +1,4 @@ -use crate::app::router::rules::RuleMatcher; -use crate::session::Session; +use crate::{app::router::rules::RuleMatcher, session::Session}; #[derive(Clone)] pub struct Port { diff --git a/clash_lib/src/app/router/rules/ruleset.rs b/clash_lib/src/app/router/rules/ruleset.rs index 805f981d8..b6ed6f734 100644 --- a/clash_lib/src/app/router/rules/ruleset.rs +++ b/clash_lib/src/app/router/rules/ruleset.rs @@ -1,6 +1,10 @@ -use crate::app::remote_content_manager::providers::rule_provider::ThreadSafeRuleProvider; -use crate::app::router::rules::RuleMatcher; -use crate::session::Session; +use crate::{ + app::{ + remote_content_manager::providers::rule_provider::ThreadSafeRuleProvider, + router::rules::RuleMatcher, + }, + session::Session, +}; #[derive(Clone)] pub struct RuleSet { @@ -10,7 +14,11 @@ pub struct RuleSet { } impl RuleSet { - pub fn new(rule_set: String, target: String, rule_provider: ThreadSafeRuleProvider) -> Self { + pub fn new( + rule_set: String, + target: String, + rule_provider: ThreadSafeRuleProvider, + ) -> Self { Self { rule_set, target, diff --git a/clash_lib/src/common/crypto.rs b/clash_lib/src/common/crypto.rs index e1c5e107e..3973909e3 100644 --- a/clash_lib/src/common/crypto.rs +++ b/clash_lib/src/common/crypto.rs @@ -1,38 +1,51 @@ use aes::cipher::{AsyncStreamCipher, KeyIvInit}; -use aes_gcm::aes::cipher::Unsigned; -use aes_gcm::{AeadInPlace, KeyInit}; +use aes_gcm::{aes::cipher::Unsigned, AeadInPlace, KeyInit}; use anyhow::Ok; -pub fn aes_cfb_encrypt(key: &[u8], iv: &[u8], data: &mut [u8]) -> anyhow::Result<()> { +pub fn aes_cfb_encrypt( + key: &[u8], + iv: &[u8], + data: &mut [u8], +) -> anyhow::Result<()> { match key.len() { 16 => { - cfb_mode::Encryptor::::new(key.into(), iv.into()).encrypt(data); + cfb_mode::Encryptor::::new(key.into(), iv.into()) + .encrypt(data); Ok(()) } 24 => { - cfb_mode::Encryptor::::new(key.into(), iv.into()).encrypt(data); + cfb_mode::Encryptor::::new(key.into(), iv.into()) + .encrypt(data); Ok(()) } 32 => { - cfb_mode::Encryptor::::new(key.into(), iv.into()).encrypt(data); + cfb_mode::Encryptor::::new(key.into(), iv.into()) + .encrypt(data); Ok(()) } _ => anyhow::bail!("invalid key length"), } } -pub fn aes_cfb_decrypt(key: &[u8], iv: &[u8], data: &mut [u8]) -> anyhow::Result<()> { +pub fn aes_cfb_decrypt( + key: &[u8], + iv: &[u8], + data: &mut [u8], +) -> anyhow::Result<()> { match key.len() { 16 => { - cfb_mode::Decryptor::::new(key.into(), iv.into()).decrypt(data); + cfb_mode::Decryptor::::new(key.into(), iv.into()) + .decrypt(data); Ok(()) } 24 => { - cfb_mode::Decryptor::::new(key.into(), iv.into()).decrypt(data); + cfb_mode::Decryptor::::new(key.into(), iv.into()) + .decrypt(data); Ok(()) } 32 => { - cfb_mode::Decryptor::::new(key.into(), iv.into()).decrypt(data); + cfb_mode::Decryptor::::new(key.into(), iv.into()) + .decrypt(data); Ok(()) } _ => anyhow::bail!("invalid key length"), @@ -104,7 +117,12 @@ pub trait AeadCipherHelper: AeadInPlace { fn new_with_slice(key: &[u8]) -> Self; /// it's up to the caller to ensure that the buffer is large enough /// i.e. buffer.len() >= plaintext.len() + Self::TagSize::to_usize() - fn encrypt_in_place_with_slice(&self, nonce: &[u8], aad: &[u8], buffer: &mut [u8]) { + fn encrypt_in_place_with_slice( + &self, + nonce: &[u8], + aad: &[u8], + buffer: &mut [u8], + ) { let tag_pos = buffer.len() - Self::TagSize::to_usize(); let (msg, tag) = buffer.split_at_mut(tag_pos); let x = self @@ -183,7 +201,8 @@ mod tests { let ad = "abc".as_bytes(); let encrypted = aes_gcm_encrypt(key, nonce, data, Some(ad)).expect("sealed"); - let decrypted = aes_gcm_decrypt(key, nonce, &encrypted, Some(ad)).expect("opened"); + let decrypted = + aes_gcm_decrypt(key, nonce, &encrypted, Some(ad)).expect("opened"); assert_eq!(decrypted, data); } diff --git a/clash_lib/src/common/http.rs b/clash_lib/src/common/http.rs index 3fab427a9..692ed4d8a 100644 --- a/clash_lib/src/common/http.rs +++ b/clash_lib/src/common/http.rs @@ -21,9 +21,10 @@ use crate::{ pub struct LocalConnector(pub ThreadSafeDNSResolver); impl Service for LocalConnector { - type Response = AnyStream; type Error = std::io::Error; - type Future = Pin> + Send>>; + type Future = + Pin> + Send>>; + type Response = AnyStream; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -66,7 +67,9 @@ impl Connection for AnyStream { pub type HttpClient = hyper::Client>; -pub fn new_http_client(dns_resolver: ThreadSafeDNSResolver) -> std::io::Result { +pub fn new_http_client( + dns_resolver: ThreadSafeDNSResolver, +) -> std::io::Result { use std::sync::Arc; use super::tls::GLOBAL_ROOT_STORE; diff --git a/clash_lib/src/common/io.rs b/clash_lib/src/common/io.rs index 1281c05e8..b3f3beb49 100644 --- a/clash_lib/src/common/io.rs +++ b/clash_lib/src/common/io.rs @@ -1,9 +1,11 @@ /// copy of https://github.com/eycorsican/leaf/blob/a77a1e497ae034f3a2a89c8628d5e7ebb2af47f0/leaf/src/common/io.rs use std::future::Future; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::Duration; +use std::{ + io, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; use futures::ready; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; @@ -114,8 +116,9 @@ impl CopyBuffer { Poll::Ready(Ok(_)) => (), Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), Poll::Pending => { - // Try flushing when the reader has no progress to avoid deadlock - // when the reader depends on buffered writer. + // Try flushing when the reader has no progress to avoid + // deadlock when the reader + // depends on buffered writer. if self.need_flush { ready!(writer.as_mut().poll_flush(cx))?; self.need_flush = false; @@ -137,7 +140,8 @@ impl CopyBuffer { // If our buffer has some data, let's write it out! while self.pos < self.cap { let me = &mut *self; - let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?; + let i = + ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?; if i == 0 { return Poll::Ready(Err(io::Error::new( io::ErrorKind::WriteZero, @@ -222,14 +226,17 @@ where continue; } Poll::Ready(Err(err)) => { - return Poll::Ready(Err(CopyBidirectionalError::LeftClosed(err))) + return Poll::Ready(Err( + CopyBidirectionalError::LeftClosed(err), + )) } Poll::Pending => { if let Some(delay) = a_to_b_delay { match delay.as_mut().poll(cx) { Poll::Ready(()) => { - *a_to_b = - TransferState::ShuttingDown(buf.amount_transfered()); + *a_to_b = TransferState::ShuttingDown( + buf.amount_transfered(), + ); continue; } Poll::Pending => (), @@ -244,12 +251,15 @@ where Poll::Ready(Ok(())) => { *a_to_b_count += *count; *a_to_b = TransferState::Done; - b_to_a_delay - .replace(Box::pin(tokio::time::sleep(*b_to_a_timeout_duration))); + b_to_a_delay.replace(Box::pin(tokio::time::sleep( + *b_to_a_timeout_duration, + ))); continue; } Poll::Ready(Err(err)) => { - return Poll::Ready(Err(CopyBidirectionalError::LeftClosed(err))) + return Poll::Ready(Err( + CopyBidirectionalError::LeftClosed(err), + )) } Poll::Pending => (), } @@ -266,14 +276,17 @@ where continue; } Poll::Ready(Err(err)) => { - return Poll::Ready(Err(CopyBidirectionalError::RightClosed(err))) + return Poll::Ready(Err( + CopyBidirectionalError::RightClosed(err), + )) } Poll::Pending => { if let Some(delay) = b_to_a_delay { match delay.as_mut().poll(cx) { Poll::Ready(()) => { - *b_to_a = - TransferState::ShuttingDown(buf.amount_transfered()); + *b_to_a = TransferState::ShuttingDown( + buf.amount_transfered(), + ); continue; } Poll::Pending => (), @@ -288,12 +301,15 @@ where Poll::Ready(Ok(())) => { *b_to_a_count += *count; *b_to_a = TransferState::Done; - a_to_b_delay - .replace(Box::pin(tokio::time::sleep(*a_to_b_timeout_duration))); + a_to_b_delay.replace(Box::pin(tokio::time::sleep( + *a_to_b_timeout_duration, + ))); continue; } Poll::Ready(Err(err)) => { - return Poll::Ready(Err(CopyBidirectionalError::RightClosed(err))) + return Poll::Ready(Err( + CopyBidirectionalError::RightClosed(err), + )) } Poll::Pending => (), } diff --git a/clash_lib/src/common/mmdb.rs b/clash_lib/src/common/mmdb.rs index 907045357..92f76e192 100644 --- a/clash_lib/src/common/mmdb.rs +++ b/clash_lib/src/common/mmdb.rs @@ -40,7 +40,9 @@ impl Mmdb { info!("downloading mmdb from {}", url); Self::download(url, &mmdb_file, http_client) .await - .map_err(|x| Error::InvalidConfig(format!("mmdb download failed: {}", x)))?; + .map_err(|x| { + Error::InvalidConfig(format!("mmdb download failed: {}", x)) + })?; } else { return Err(Error::InvalidConfig(format!( "mmdb `{}` not found and mmdb_download_url is not set", @@ -64,11 +66,14 @@ impl Mmdb { fs::remove_file(&mmdb_file)?; if let Some(url) = download_url.as_ref() { info!("downloading mmdb from {}", url); - Self::download(url, &mmdb_file, http_client) - .await - .map_err(|x| { - Error::InvalidConfig(format!("mmdb download failed: {}", x)) - })?; + Self::download(url, &mmdb_file, http_client).await.map_err( + |x| { + Error::InvalidConfig(format!( + "mmdb download failed: {}", + x + )) + }, + )?; Ok(maxminddb::Reader::open_readfile(&path).map_err(|x| { Error::InvalidConfig(format!( "cant open mmdb `{}`: {}", @@ -93,7 +98,11 @@ impl Mmdb { } #[async_recursion] - async fn download

(url: &str, path: P, http_client: &HttpClient) -> anyhow::Result<()> + async fn download

( + url: &str, + path: P, + http_client: &HttpClient, + ) -> anyhow::Result<()> where P: AsRef + std::marker::Send, { @@ -117,9 +126,11 @@ impl Mmdb { } if !res.status().is_success() { - return Err( - Error::InvalidConfig(format!("mmdb download failed: {}", res.status())).into(), - ); + return Err(Error::InvalidConfig(format!( + "mmdb download failed: {}", + res.status() + )) + .into()); } debug!("downloading mmdb to {}", path.as_ref().to_string_lossy()); diff --git a/clash_lib/src/common/tls.rs b/clash_lib/src/common/tls.rs index 6dbfedd50..f3a7cf7a0 100644 --- a/clash_lib/src/common/tls.rs +++ b/clash_lib/src/common/tls.rs @@ -1,6 +1,9 @@ use once_cell::sync::Lazy; use rustls::{ - client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, WebPkiVerifier}, + client::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, + WebPkiVerifier, + }, DigitallySignedStruct, OwnedTrustAnchor, RootCertStore, }; use tracing::warn; @@ -8,7 +11,8 @@ use tracing::warn; use rustls::{Certificate, ServerName}; use std::{sync::Arc, time::SystemTime}; -pub static GLOBAL_ROOT_STORE: Lazy> = Lazy::new(global_root_store); +pub static GLOBAL_ROOT_STORE: Lazy> = + Lazy::new(global_root_store); fn global_root_store() -> Arc { let mut root_store = RootCertStore::empty(); @@ -70,7 +74,8 @@ impl ServerCertVerifier for NoHostnameTlsVerifier { ocsp_response: &[u8], now: SystemTime, ) -> Result { - let verifier = WebPkiVerifier::new(rustls::RootCertStore { roots: vec![] }, None); + let verifier = + WebPkiVerifier::new(rustls::RootCertStore { roots: vec![] }, None); match verifier.verify_server_cert( end_entity, intermediates, diff --git a/clash_lib/src/common/trie.rs b/clash_lib/src/common/trie.rs index 4f1083327..029baa7c5 100644 --- a/clash_lib/src/common/trie.rs +++ b/clash_lib/src/common/trie.rs @@ -130,7 +130,8 @@ impl StringTrie { } if let Some(c) = node.get_child(parts.last().unwrap().to_owned()) { - if let Some(n) = Self::search_inner(c, parts[0..parts.len() - 1].into()) { + if let Some(n) = Self::search_inner(c, parts[0..parts.len() - 1].into()) + { if n.data.is_some() { return Some(n); } @@ -138,7 +139,8 @@ impl StringTrie { } if let Some(c) = node.get_child(WILDCARD) { - if let Some(n) = Self::search_inner(c, parts[0..parts.len() - 1].into()) { + if let Some(n) = Self::search_inner(c, parts[0..parts.len() - 1].into()) + { if n.data.is_some() { return Some(n); } @@ -242,7 +244,9 @@ mod tests { tree.insert(d, Arc::new(idx)); } - let assert_fn = |k: &str| -> Arc { tree.search(k).unwrap().data.clone().unwrap() }; + let assert_fn = |k: &str| -> Arc { + tree.search(k).unwrap().data.clone().unwrap() + }; assert_eq!(assert_fn("test.dev"), Arc::new(0)); assert_eq!(assert_fn("foo.bar.dev"), Arc::new(0)); diff --git a/clash_lib/src/config/def.rs b/clash_lib/src/config/def.rs index 22fb41206..7460ed10d 100644 --- a/clash_lib/src/config/def.rs +++ b/clash_lib/src/config/def.rs @@ -1,7 +1,5 @@ use crate::Error; -use std::path::PathBuf; -use std::str::FromStr; -use std::{collections::HashMap, fmt::Display}; +use std::{collections::HashMap, fmt::Display, path::PathBuf, str::FromStr}; use serde::{Deserialize, Serialize}; use serde_yaml::Value; @@ -73,8 +71,8 @@ impl Display for LogLevel { /// # ipv6: false # when the false, response to AAAA questions will be empty -/// # These nameservers are used to resolve the DNS nameserver hostnames below. -/// # Specify IP addresses only +/// # These nameservers are used to resolve the DNS nameserver hostnames +/// below. # Specify IP addresses only /// default-nameserver: /// - 114.114.114.114 /// - 8.8.8.8 @@ -113,9 +111,7 @@ impl Display for LogLevel { /// store-fake-ip: false /// proxy-groups: -/// - name: "relay" -/// type: relay -/// proxies: +/// - name: "relay" type: relay proxies: /// - "plain-vmess" /// - "ws-vmess" /// - "auto" @@ -124,32 +120,24 @@ impl Display for LogLevel { /// - "select" /// - DIRECT -/// - name: "relay-one" -/// type: relay -/// use: +/// - name: "relay-one" type: relay use: /// - "file-provider" -/// - name: "auto" -/// type: url-test -/// use: +/// - name: "auto" type: url-test use: /// - "file-provider" /// proxies: /// - DIRECT /// url: "http://www.gstatic.com/generate_204" /// interval: 300 -/// - name: "fallback-auto" -/// type: fallback -/// use: +/// - name: "fallback-auto" type: fallback use: /// - "file-provider" /// proxies: /// - DIRECT /// url: "http://www.gstatic.com/generate_204" /// interval: 300 -/// - name: "load-balance" -/// type: load-balance -/// use: +/// - name: "load-balance" type: load-balance use: /// - "file-provider" /// proxies: /// - DIRECT @@ -157,96 +145,39 @@ impl Display for LogLevel { /// url: "http://www.gstatic.com/generate_204" /// interval: 300 -/// - name: select -/// type: select -/// use: +/// - name: select type: select use: /// - "file-provider" -/// - name: test 🌏 -/// type: select -/// use: +/// - name: test 🌏 type: select use: /// - "file-provider" /// proxies: /// - DIRECT /// proxies: -/// - name: plain-vmess -/// type: vmess -/// server: 10.0.0.13 -/// port: 16823 -/// uuid: b831381d-6324-4d53-ad4f-8cda48b30811 -/// alterId: 0 -/// cipher: auto -/// udp: true -/// skip-cert-verify: true -/// - name: ws-vmess -/// type: vmess -/// server: 10.0.0.13 -/// port: 16824 -/// uuid: b831381d-6324-4d53-ad4f-8cda48b30811 -/// alterId: 0 -/// cipher: auto -/// udp: true -/// skip-cert-verify: true -/// network: ws -/// ws-opts: -/// path: /api/v3/download.getFile -/// headers: -/// Host: www.amazon.com - -/// - name: tls-vmess -/// type: vmess -/// server: 10.0.0.13 -/// port: 8443 -/// uuid: 23ad6b10-8d1a-40f7-8ad0-e3e35cd38297 -/// alterId: 0 -/// cipher: auto -/// udp: true -/// skip-cert-verify: true -/// tls: true - -/// - name: h2-vmess -/// type: vmess -/// server: 10.0.0.13 -/// port: 8444 -/// uuid: b831381d-6324-4d53-ad4f-8cda48b30811 -/// alterId: 0 -/// cipher: auto -/// udp: true +/// - name: plain-vmess type: vmess server: 10.0.0.13 port: 16823 uuid: +/// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true /// skip-cert-verify: true -/// tls: true -/// network: h2 -/// h2-opts: -/// path: /ray - -/// - name: vmess-altid -/// type: vmess -/// server: tw-1.ac.laowanxiang.com -/// port: 153 -/// uuid: 46dd0dd3-2cc0-3f55-907c-d94e54877687 -/// alterId: 64 -/// cipher: auto -/// udp: true -/// network: ws -/// ws-opts: -/// 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 -/// - name: "trojan" -/// type: trojan -/// server: 10.0.0.13 -/// port: 9443 -/// password: password1 -/// udp: true -/// # sni: example.com # aka server name -/// alpn: +/// - name: ws-vmess type: vmess server: 10.0.0.13 port: 16824 uuid: +/// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true +/// skip-cert-verify: true network: ws ws-opts: path: +/// /api/v3/download.getFile headers: Host: www.amazon.com + +/// - name: tls-vmess type: vmess server: 10.0.0.13 port: 8443 uuid: +/// 23ad6b10-8d1a-40f7-8ad0-e3e35cd38297 alterId: 0 cipher: auto udp: true +/// skip-cert-verify: true tls: true + +/// - name: h2-vmess type: vmess server: 10.0.0.13 port: 8444 uuid: +/// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true +/// skip-cert-verify: true tls: true network: h2 h2-opts: path: /ray + +/// - name: vmess-altid type: vmess server: tw-1.ac.laowanxiang.com port: 153 +/// uuid: 46dd0dd3-2cc0-3f55-907c-d94e54877687 alterId: 64 cipher: auto udp: +/// true network: ws ws-opts: 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 +/// - name: "trojan" type: trojan server: 10.0.0.13 port: 9443 password: +/// password1 udp: true # sni: example.com # aka server name alpn: /// - h2 /// - http/1.1 /// skip-cert-verify: true @@ -308,9 +239,11 @@ pub struct Config { pub allow_lan: bool, /// The address that the inbound listens on /// # Note - /// - setting this to `*` will listen on all interfaces, which is essentially the same as setting it to `0.0.0.0` + /// - setting this to `*` will listen on all interfaces, which is + /// essentially the same as setting it to `0.0.0.0` /// - setting this to non local IP will enable `allow_lan` automatically - /// - and if you don't want `allow_lan` to be enabled, you should set this to `localhost` or `127.1` + /// - and if you don't want `allow_lan` to be enabled, you should set this + /// to `localhost` or `127.1` pub bind_address: String, /// Clash router working mode /// Either `rule`, `global` or `direct` @@ -391,7 +324,10 @@ impl FromStr for Config { fn from_str(s: &str) -> Result { serde_yaml::from_str(s).map_err(|x| { - Error::InvalidConfig(format!("cound not parse config content {}: {}", s, x)) + Error::InvalidConfig(format!( + "cound not parse config content {}: {}", + s, x + )) }) } } @@ -443,8 +379,8 @@ pub enum DNSListen { } /// DNS client/server settings -/// This section is optional. When not present, the DNS server will be disabled and system DNS config will be used -/// # Example +/// This section is optional. When not present, the DNS server will be disabled +/// and system DNS config will be used # Example /// ```yaml /// dns: /// enable: true @@ -472,7 +408,8 @@ pub struct DNS { pub fallback: Vec, /// Fallback DNS filter pub fallback_filter: FallbackFilter, - /// DNS server listening address. If not present, the DNS server will be disabled. + /// DNS server listening address. If not present, the DNS server will be + /// disabled. pub listen: Option, /// Whether to use fake IP addresses pub enhanced_mode: DNSMode, @@ -499,7 +436,10 @@ impl Default for DNS { enhanced_mode: Default::default(), fake_ip_range: String::from("198.18.0.1/16"), fake_ip_filter: Default::default(), - default_nameserver: vec![String::from("114.114.114.114"), String::from("8.8.8.8")], + default_nameserver: vec![ + String::from("114.114.114.114"), + String::from("8.8.8.8"), + ], nameserver_policy: Default::default(), } } @@ -1044,7 +984,8 @@ rules: - MATCH,auto "###; - let des: Config = serde_yaml::from_str(example_cfg).expect("should parse yaml"); + let des: Config = + serde_yaml::from_str(example_cfg).expect("should parse yaml"); assert_eq!(des.port.expect("invalid port"), 7890); assert_eq!(des.dns.fallback_filter.geo_ip_code, String::from("CN")); assert_eq!(des.proxy.len(), 14); diff --git a/clash_lib/src/config/internal/config.rs b/clash_lib/src/config/internal/config.rs index 6cd527d28..861603d76 100644 --- a/clash_lib/src/config/internal/config.rs +++ b/clash_lib/src/config/internal/config.rs @@ -1,26 +1,27 @@ use std::collections::HashMap; -use std::fmt::Display; -use std::net::IpAddr; -use std::str::FromStr; +use std::{fmt::Display, net::IpAddr, str::FromStr}; -use serde::de::value::MapDeserializer; -use serde::{Deserialize, Serialize}; +use serde::{de::value::MapDeserializer, Deserialize, Serialize}; use serde_yaml::Value; -use crate::app::remote_content_manager::providers::rule_provider::RuleSetBehavior; -use crate::common::auth; -use crate::config::def::{self}; -use crate::config::internal::proxy::{OutboundProxy, PROXY_DIRECT, PROXY_REJECT}; -use crate::config::internal::rule::RuleType; -use crate::proxy::utils::Interface; use crate::{ - app::dns, - config::def::{LogLevel, RunMode}, + app::{dns, remote_content_manager::providers::rule_provider::RuleSetBehavior}, + common::auth, + config::{ + def::{self, LogLevel, RunMode}, + internal::{ + proxy::{OutboundProxy, PROXY_DIRECT, PROXY_REJECT}, + rule::RuleType, + }, + }, + proxy::utils::Interface, Error, }; -use super::proxy::{map_serde_error, OutboundProxyProtocol, OutboundProxyProviderDef}; +use super::proxy::{ + map_serde_error, OutboundProxyProtocol, OutboundProxyProviderDef, +}; pub struct Config { pub general: General, @@ -41,7 +42,8 @@ pub struct Config { impl Config { fn validate(self) -> Result { for r in self.rules.iter() { - if !self.proxies.contains_key(r.target()) && !self.proxy_groups.contains_key(r.target()) + if !self.proxies.contains_key(r.target()) + && !self.proxy_groups.contains_key(r.target()) { return Err(Error::InvalidConfig(format!( "proxy `{}` referenced in a rule was not found", @@ -57,7 +59,8 @@ impl TryFrom for Config { type Error = crate::Error; fn try_from(c: def::Config) -> Result { - let mut proxy_names = vec![String::from(PROXY_DIRECT), String::from(PROXY_REJECT)]; + let mut proxy_names = + vec![String::from(PROXY_DIRECT), String::from(PROXY_REJECT)]; #[allow(deprecated)] Self { general: General { @@ -92,8 +95,15 @@ impl TryFrom for Config { dns: (&c).try_into()?, experimental: c.experimental, tun: match c.tun { - Some(mapping) => TunConfig::deserialize(MapDeserializer::new(mapping.into_iter())) - .map_err(|e| Error::InvalidConfig(format!("invalid tun config: {}", e)))?, + Some(mapping) => { + TunConfig::deserialize(MapDeserializer::new(mapping.into_iter())) + .map_err(|e| { + Error::InvalidConfig(format!( + "invalid tun config: {}", + e + )) + })? + } None => TunConfig::default(), }, profile: Profile { @@ -111,18 +121,25 @@ impl TryFrom for Config { .rule_provider .map(|m| { m.into_iter() - .try_fold(HashMap::new(), |mut rv, (name, mut body)| { - body.insert("name".to_owned(), serde_yaml::Value::String(name.clone())); - let provider = RuleProviderDef::try_from(body).map_err(|x| { - Error::InvalidConfig(format!( - "invalid rule provider {}: {}", - name, x - )) - })?; - rv.insert(name, provider); - Ok::, Error>(rv) - }) - .expect("proxy provider parse error") + .try_fold(HashMap::new(), |mut rv, (name, mut body)| { + body.insert( + "name".to_owned(), + serde_yaml::Value::String(name.clone()), + ); + let provider = RuleProviderDef::try_from(body) + .map_err(|x| { + Error::InvalidConfig(format!( + "invalid rule provider {}: {}", + name, x + )) + })?; + rv.insert(name, provider); + Ok::< + HashMap, + Error, + >(rv) + }) + .expect("proxy provider parse error") }) .unwrap_or_default(), users: c @@ -147,7 +164,9 @@ impl TryFrom for Config { ), ]), |mut rv, x| { - let proxy = OutboundProxy::ProxyServer(OutboundProxyProtocol::try_from(x)?); + let proxy = OutboundProxy::ProxyServer( + OutboundProxyProtocol::try_from(x)?, + ); let name = proxy.name(); if rv.contains_key(name.as_str()) { return Err(Error::InvalidConfig(format!( @@ -163,19 +182,22 @@ impl TryFrom for Config { proxy_groups: c.proxy_group.into_iter().try_fold( HashMap::::new(), |mut rv, mapping| { - let group = OutboundProxy::ProxyGroup(mapping.clone().try_into().map_err( - |x: Error| { + let group = OutboundProxy::ProxyGroup( + mapping.clone().try_into().map_err(|x: Error| { if let Some(name) = mapping.get("name") { Error::InvalidConfig(format!( "proxy group: {}: {}", - name.as_str().expect("proxy group name must be string"), + name.as_str() + .expect("proxy group name must be string"), x )) } else { - Error::InvalidConfig("proxy group name missing".to_string()) + Error::InvalidConfig( + "proxy group name missing".to_string(), + ) } - }, - )?); + })?, + ); proxy_names.push(group.name()); rv.insert(group.name().to_string(), group); Ok::, Error>(rv) @@ -188,16 +210,25 @@ impl TryFrom for Config { .map(|m| { m.into_iter() .try_fold(HashMap::new(), |mut rv, (name, mut body)| { - body.insert("name".to_owned(), serde_yaml::Value::String(name.clone())); - let provider = - OutboundProxyProviderDef::try_from(body).map_err(|x| { + body.insert( + "name".to_owned(), + serde_yaml::Value::String(name.clone()), + ); + let provider = OutboundProxyProviderDef::try_from(body) + .map_err(|x| { Error::InvalidConfig(format!( "invalid proxy provider {}: {}", name, x )) })?; rv.insert(name, provider); - Ok::, Error>(rv) + Ok::< + HashMap< + std::string::String, + OutboundProxyProviderDef, + >, + Error, + >(rv) }) .expect("proxy provider parse error") }) @@ -283,7 +314,9 @@ impl FromStr for BindAddress { fn from_str(s: &str) -> Result { match s { "*" => Ok(Self::Any), - "localhost" => Ok(Self::One(Interface::IpAddr(IpAddr::from([127, 0, 0, 1])))), + "localhost" => { + Ok(Self::One(Interface::IpAddr(IpAddr::from([127, 0, 0, 1])))) + } _ => { if let Ok(ip) = s.parse::() { Ok(BindAddress::One(Interface::IpAddr(ip))) diff --git a/clash_lib/src/config/internal/proxy.rs b/clash_lib/src/config/internal/proxy.rs index 918e48cff..6ab8df732 100644 --- a/clash_lib/src/config/internal/proxy.rs +++ b/clash_lib/src/config/internal/proxy.rs @@ -1,11 +1,10 @@ -use crate::common::utils::default_bool_true; -use crate::config::utils; -use crate::Error; -use serde::de::value::MapDeserializer; -use serde::Deserialize; +use crate::{common::utils::default_bool_true, config::utils, Error}; +use serde::{de::value::MapDeserializer, Deserialize}; use serde_yaml::Value; -use std::collections::HashMap; -use std::fmt::{Display, Formatter}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter}, +}; use uuid::Uuid; pub const PROXY_DIRECT: &str = "DIRECT"; @@ -27,7 +26,9 @@ impl OutboundProxy { } } -pub fn map_serde_error(name: String) -> impl FnOnce(serde_yaml::Error) -> crate::Error { +pub fn map_serde_error( + name: String, +) -> impl FnOnce(serde_yaml::Error) -> crate::Error { move |x| { if let Some(loc) = x.location() { Error::InvalidConfig(format!( @@ -438,7 +439,9 @@ impl TryFrom> for OutboundProxyProviderDef { "missing field `name` in outbound proxy provider".to_owned(), ))? .to_owned(); - OutboundProxyProviderDef::deserialize(MapDeserializer::new(mapping.into_iter())) - .map_err(map_serde_error(name)) + OutboundProxyProviderDef::deserialize(MapDeserializer::new( + mapping.into_iter(), + )) + .map_err(map_serde_error(name)) } } diff --git a/clash_lib/src/config/internal/rule.rs b/clash_lib/src/config/internal/rule.rs index 3fd28b859..9b7a0d231 100644 --- a/clash_lib/src/config/internal/rule.rs +++ b/clash_lib/src/config/internal/rule.rs @@ -76,7 +76,9 @@ impl RuleType { impl Display for RuleType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - RuleType::Domain { domain, target } => write!(f, "DOMAIN,{},{}", domain, target), + RuleType::Domain { domain, target } => { + write!(f, "DOMAIN,{},{}", domain, target) + } RuleType::DomainSuffix { .. } => write!(f, "DOMAIN-SUFFIX"), RuleType::DomainKeyword { .. } => write!(f, "DOMAIN-KEYWORD"), RuleType::GeoIP { .. } => write!(f, "GEOIP"), diff --git a/clash_lib/src/config/utils.rs b/clash_lib/src/config/utils.rs index 2c9fffa9c..c4a3727c0 100644 --- a/clash_lib/src/config/utils.rs +++ b/clash_lib/src/config/utils.rs @@ -1,7 +1,6 @@ use serde::Deserialize; -use std::fmt::Display; -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; pub fn deserialize_u64<'de, T, D>(deserializer: D) -> Result where diff --git a/clash_lib/src/lib.rs b/clash_lib/src/lib.rs index 7a62edece..5fe662bec 100644 --- a/clash_lib/src/lib.rs +++ b/clash_lib/src/lib.rs @@ -3,32 +3,32 @@ #[macro_use] extern crate anyhow; -use crate::app::dispatcher::Dispatcher; -use crate::app::dns; -use crate::app::inbound::manager::InboundManager; -use crate::app::outbound::manager::OutboundManager; -use crate::app::router::Router; -use crate::config::def; -use crate::config::internal::proxy::OutboundProxy; -use crate::config::internal::InternalConfig; -use app::dispatcher::StatisticsManager; -use app::dns::SystemResolver; -use app::profile; -use common::auth; -use common::http::new_http_client; -use common::mmdb; +use crate::{ + app::{ + dispatcher::Dispatcher, dns, inbound::manager::InboundManager, + outbound::manager::OutboundManager, router::Router, + }, + config::{ + def, + internal::{proxy::OutboundProxy, InternalConfig}, + }, +}; +use app::{dispatcher::StatisticsManager, dns::SystemResolver, profile}; +use common::{auth, http::new_http_client, mmdb}; use config::def::LogLevel; use proxy::tun::get_tun_runner; -use std::io; -use std::path::PathBuf; -use std::sync::{Arc, OnceLock}; +use std::{ + io, + path::PathBuf, + sync::{Arc, OnceLock}, +}; use thiserror::Error; -use tokio::sync::{broadcast, mpsc, oneshot, Mutex}; -use tokio::task::JoinHandle; -use tracing::debug; -use tracing::error; -use tracing::info; +use tokio::{ + sync::{broadcast, mpsc, oneshot, Mutex}, + task::JoinHandle, +}; +use tracing::{debug, error, info}; mod app; mod common; @@ -36,10 +36,10 @@ 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 config::{ + def::{Config as ClashConfigDef, DNS as ClashDNSConfigDef}, + DNSListen as ClashDNSListen, RuntimeConfig as ClashRuntimeConfig, +}; #[derive(Error, Debug)] pub enum Error { @@ -86,7 +86,9 @@ impl Config { match self { Config::Def(c) => c.try_into(), Config::Internal(c) => Ok(c), - Config::File(file) => TryInto::::try_into(PathBuf::from(file))?.try_into(), + Config::File(file) => { + TryInto::::try_into(PathBuf::from(file))?.try_into() + } Config::Str(s) => s.parse::()?.try_into(), } } @@ -106,7 +108,8 @@ pub struct RuntimeController { shutdown_tx: mpsc::Sender<()>, } -static RUNTIME_CONTROLLER: OnceLock> = OnceLock::new(); +static RUNTIME_CONTROLLER: OnceLock> = + OnceLock::new(); pub fn start(opts: Options) -> Result<(), Error> { let rt = match opts.rt.as_ref().unwrap_or(&TokioRuntime::MultiThread) { @@ -139,7 +142,8 @@ pub fn shutdown() -> bool { async fn start_async(opts: Options) -> Result<(), Error> { let (shutdown_tx, mut shutdown_rx) = mpsc::channel(1); - let _ = RUNTIME_CONTROLLER.set(std::sync::RwLock::new(RuntimeController { shutdown_tx })); + let _ = RUNTIME_CONTROLLER + .set(std::sync::RwLock::new(RuntimeController { shutdown_tx })); let config: InternalConfig = opts.config.try_parse()?; @@ -149,10 +153,14 @@ async fn start_async(opts: Options) -> Result<(), Error> { let log_collector = app::logging::EventCollector::new(vec![log_tx.clone()]); - let _g = - app::logging::setup_logging(config.general.log_level, log_collector, &cwd, opts.log_file) - .map_err(|x| eprintln!("failed to setup logging: {}", x)) - .unwrap_or_default(); + let _g = app::logging::setup_logging( + config.general.log_level, + log_collector, + &cwd, + opts.log_file, + ) + .map_err(|x| eprintln!("failed to setup logging: {}", x)) + .unwrap_or_default(); let default_panic = std::panic::take_hook(); std::panic::set_hook(Box::new(move |info| { @@ -166,7 +174,8 @@ async fn start_async(opts: Options) -> Result<(), Error> { debug!("initializing dns resolver"); let system_resolver = Arc::new(SystemResolver::new().map_err(|x| Error::DNSError(x.to_string()))?); - let client = new_http_client(system_resolver).map_err(|x| Error::DNSError(x.to_string()))?; + let client = new_http_client(system_resolver) + .map_err(|x| Error::DNSError(x.to_string()))?; debug!("initializing mmdb"); let cwd = PathBuf::from(cwd); @@ -186,7 +195,8 @@ async fn start_async(opts: Options) -> Result<(), Error> { ); let dns_resolver = - dns::Resolver::new_resolver(&config.dns, cache_store.clone(), mmdb.clone()).await; + dns::Resolver::new_resolver(&config.dns, cache_store.clone(), mmdb.clone()) + .await; debug!("initializing outbound manager"); let outbound_manager = Arc::new( @@ -250,13 +260,15 @@ async fn start_async(opts: Options) -> Result<(), Error> { let inbound_runner = inbound_manager.lock().await.get_runner()?; let inbound_listener_handle = tokio::spawn(inbound_runner); - let tun_runner = get_tun_runner(config.tun, dispatcher.clone(), dns_resolver.clone())?; + let tun_runner = + get_tun_runner(config.tun, dispatcher.clone(), dns_resolver.clone())?; let tun_runner_handle = tun_runner.map(tokio::spawn); debug!("initializing dns listener"); - let dns_listener_handle = dns::get_dns_listener(config.dns, dns_resolver.clone()) - .await - .map(tokio::spawn); + let dns_listener_handle = + dns::get_dns_listener(config.dns, dns_resolver.clone()) + .await + .map(tokio::spawn); let (reload_tx, mut reload_rx) = mpsc::channel(1); @@ -315,10 +327,11 @@ async fn start_async(opts: Options) -> Result<(), Error> { }; debug!("reloading dns resolver"); - let system_resolver = - Arc::new(SystemResolver::new().map_err(|x| Error::DNSError(x.to_string()))?); - let client = - new_http_client(system_resolver).map_err(|x| Error::DNSError(x.to_string()))?; + let system_resolver = Arc::new( + SystemResolver::new().map_err(|x| Error::DNSError(x.to_string()))?, + ); + let client = new_http_client(system_resolver) + .map_err(|x| Error::DNSError(x.to_string()))?; debug!("reloading mmdb"); let mmdb = Arc::new( @@ -336,8 +349,12 @@ async fn start_async(opts: Options) -> Result<(), Error> { config.profile.store_selected, ); - let dns_resolver = - dns::Resolver::new_resolver(&config.dns, cache_store.clone(), mmdb.clone()).await; + let dns_resolver = dns::Resolver::new_resolver( + &config.dns, + cache_store.clone(), + mmdb.clone(), + ) + .await; debug!("reloading outbound manager"); let outbound_manager = Arc::new( @@ -389,7 +406,8 @@ async fn start_async(opts: Options) -> Result<(), Error> { statistics_manager.clone(), )); - let authenticator = Arc::new(auth::PlainAuthenticator::new(config.users)); + let authenticator = + Arc::new(auth::PlainAuthenticator::new(config.users)); debug!("reloading inbound manager"); let inbound_manager = Arc::new(Mutex::new(InboundManager::new( @@ -421,14 +439,18 @@ async fn start_async(opts: Options) -> Result<(), Error> { .get_runner() .map(tokio::spawn)?; - let tun_runner_handle = - get_tun_runner(config.tun, dispatcher.clone(), dns_resolver.clone())? - .map(tokio::spawn); + let tun_runner_handle = get_tun_runner( + config.tun, + dispatcher.clone(), + dns_resolver.clone(), + )? + .map(tokio::spawn); debug!("reloading dns listener"); - let dns_listener_handle = dns::get_dns_listener(config.dns, dns_resolver.clone()) - .await - .map(tokio::spawn); + let dns_listener_handle = + dns::get_dns_listener(config.dns, dns_resolver.clone()) + .await + .map(tokio::spawn); debug!("reloading api listener"); let api_listener_handle = app::api::get_api_runner( @@ -463,8 +485,7 @@ async fn start_async(opts: Options) -> Result<(), Error> { #[cfg(test)] mod tests { use crate::{shutdown, start, Config, Options}; - use std::thread; - use std::time::Duration; + use std::{thread, time::Duration}; #[test] fn start_and_stop() { diff --git a/clash_lib/src/proxy/converters/trojan.rs b/clash_lib/src/proxy/converters/trojan.rs index 212ebdf19..9eb2f38cf 100644 --- a/clash_lib/src/proxy/converters/trojan.rs +++ b/clash_lib/src/proxy/converters/trojan.rs @@ -50,13 +50,18 @@ impl TryFrom<&OutboundTrojan> for AnyOutboundHandler { .as_ref() .map(|x| { Transport::Ws(WsOption { - path: x.path.as_ref().map(|x| x.to_owned()).unwrap_or_default(), + path: x + .path + .as_ref() + .map(|x| x.to_owned()) + .unwrap_or_default(), headers: x .headers .as_ref() .map(|x| x.to_owned()) .unwrap_or_default(), - max_early_data: x.max_early_data.unwrap_or_default() as usize, + max_early_data: x.max_early_data.unwrap_or_default() + as usize, early_data_header_name: x .early_data_header_name .as_ref() diff --git a/clash_lib/src/proxy/converters/tuic.rs b/clash_lib/src/proxy/converters/tuic.rs index 2e194608a..42db29bf9 100644 --- a/clash_lib/src/proxy/converters/tuic.rs +++ b/clash_lib/src/proxy/converters/tuic.rs @@ -40,9 +40,14 @@ impl TryFrom<&OutboundTuic> for AnyOutboundHandler { .clone() .map(|v| v.into_iter().map(|alpn| alpn.into_bytes()).collect()) .unwrap_or_default(), - heartbeat_interval: Duration::from_millis(s.heartbeat_interval.unwrap_or(3000)), - reduce_rtt: s.reduce_rtt.unwrap_or(false) || s.fast_open.unwrap_or(false), - request_timeout: Duration::from_millis(s.request_timeout.unwrap_or(4000)), + heartbeat_interval: Duration::from_millis( + s.heartbeat_interval.unwrap_or(3000), + ), + reduce_rtt: s.reduce_rtt.unwrap_or(false) + || s.fast_open.unwrap_or(false), + request_timeout: Duration::from_millis( + s.request_timeout.unwrap_or(4000), + ), idle_timeout: Duration::from_millis(s.request_timeout.unwrap_or(4000)), congestion_controller: s .congestion_controller @@ -58,8 +63,10 @@ impl TryFrom<&OutboundTuic> for AnyOutboundHandler { gc_interval: Duration::from_millis(s.gc_interval.unwrap_or(3000)), gc_lifetime: Duration::from_millis(s.gc_lifetime.unwrap_or(15000)), send_window: s.send_window.unwrap_or(8 * 1024 * 1024 * 2), - receive_window: VarInt::from_u64(s.receive_window.unwrap_or(8 * 1024 * 1024)) - .unwrap_or(VarInt::MAX), + receive_window: VarInt::from_u64( + s.receive_window.unwrap_or(8 * 1024 * 1024), + ) + .unwrap_or(VarInt::MAX), }) } } diff --git a/clash_lib/src/proxy/converters/vmess.rs b/clash_lib/src/proxy/converters/vmess.rs index 7a49565dd..9e5845546 100644 --- a/clash_lib/src/proxy/converters/vmess.rs +++ b/clash_lib/src/proxy/converters/vmess.rs @@ -46,13 +46,18 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { .as_ref() .map(|x| { VmessTransport::Ws(WsOption { - path: x.path.as_ref().map(|x| x.to_owned()).unwrap_or_default(), + path: x + .path + .as_ref() + .map(|x| x.to_owned()) + .unwrap_or_default(), headers: x .headers .as_ref() .map(|x| x.to_owned()) .unwrap_or_default(), - max_early_data: x.max_early_data.unwrap_or_default() as usize, + max_early_data: x.max_early_data.unwrap_or_default() + as usize, early_data_header_name: x .early_data_header_name .as_ref() @@ -73,7 +78,11 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { .as_ref() .map(|x| x.to_owned()) .unwrap_or(vec![s.server.to_owned()]), - path: x.path.as_ref().map(|x| x.to_owned()).unwrap_or_default(), + path: x + .path + .as_ref() + .map(|x| x.to_owned()) + .unwrap_or_default(), }) }) .ok_or(Error::InvalidConfig( @@ -84,7 +93,11 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { .as_ref() .map(|x| { VmessTransport::Grpc(GrpcOption { - host: s.server_name.as_ref().unwrap_or(&s.server).to_owned(), + host: s + .server_name + .as_ref() + .unwrap_or(&s.server) + .to_owned(), service_name: x .grpc_service_name .as_ref() @@ -96,7 +109,10 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { .ok_or(Error::InvalidConfig( "grpc_opts is required for grpc".to_owned(), )), - _ => Err(Error::InvalidConfig(format!("unsupported network: {}", x))), + _ => Err(Error::InvalidConfig(format!( + "unsupported network: {}", + x + ))), }) .transpose()?, tls: match s.tls.unwrap_or_default() { @@ -121,7 +137,10 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler { "ws" => Ok(vec!["http/1.1".to_owned()]), "http" => Ok(vec![]), "h2" | "grpc" => Ok(vec!["h2".to_owned()]), - _ => Err(Error::InvalidConfig(format!("unsupported network: {}", x))), + _ => Err(Error::InvalidConfig(format!( + "unsupported network: {}", + x + ))), }) .transpose()?, }), diff --git a/clash_lib/src/proxy/converters/wireguard.rs b/clash_lib/src/proxy/converters/wireguard.rs index 324c7d53e..c351b4734 100644 --- a/clash_lib/src/proxy/converters/wireguard.rs +++ b/clash_lib/src/proxy/converters/wireguard.rs @@ -35,7 +35,10 @@ impl TryFrom<&OutboundWireguard> for AnyOutboundHandler { )), }) .map_err(|x| { - Error::InvalidConfig(format!("invalid ip address: {}, {}", x, s.ip)) + Error::InvalidConfig(format!( + "invalid ip address: {}, {}", + x, s.ip + )) })??, ipv6: s .ipv6 @@ -44,12 +47,16 @@ impl TryFrom<&OutboundWireguard> for AnyOutboundHandler { x.parse::() .map(|x| match x.addr() { std::net::IpAddr::V4(_) => Err(Error::InvalidConfig( - "invalid ip address: put an v6 address here".to_owned(), + "invalid ip address: put an v6 address here" + .to_owned(), )), std::net::IpAddr::V6(v6) => Ok(v6), }) .map_err(|e| { - Error::InvalidConfig(format!("invalid ipv6 address: {}, {}", e, x)) + Error::InvalidConfig(format!( + "invalid ipv6 address: {}, {}", + e, x + )) }) .ok() }) diff --git a/clash_lib/src/proxy/datagram.rs b/clash_lib/src/proxy/datagram.rs index 15f026124..d28f7020f 100644 --- a/clash_lib/src/proxy/datagram.rs +++ b/clash_lib/src/proxy/datagram.rs @@ -1,16 +1,18 @@ -use crate::app::dns::ThreadSafeDNSResolver; -use crate::proxy::socks::Socks5UDPCodec; -use crate::proxy::{AnyOutboundDatagram, InboundDatagram}; -use crate::session::SocksAddr; +use crate::{ + app::dns::ThreadSafeDNSResolver, + proxy::{socks::Socks5UDPCodec, AnyOutboundDatagram, InboundDatagram}, + session::SocksAddr, +}; use bytes::Bytes; use futures::{ready, Sink, SinkExt, Stream, StreamExt}; -use std::fmt::{Debug, Display, Formatter}; -use std::io; -use std::net::SocketAddr; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio::io::ReadBuf; -use tokio::net::UdpSocket; +use std::{ + fmt::{Debug, Display, Formatter}, + io, + net::SocketAddr, + pin::Pin, + task::{Context, Poll}, +}; +use tokio::{io::ReadBuf, net::UdpSocket}; use tokio_util::udp::UdpFramed; #[derive(Clone)] @@ -84,7 +86,10 @@ impl Debug for InboundUdp> { impl Stream for InboundUdp> { type Item = UdpPacket; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let pin = self.get_mut(); match pin.inner.poll_next_unpin(cx) { @@ -107,7 +112,10 @@ impl Stream for InboundUdp> { impl Sink for InboundUdp> { type Error = std::io::Error; - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let pin = self.get_mut(); pin.inner.poll_ready_unpin(cx) } @@ -120,12 +128,18 @@ impl Sink for InboundUdp> { )) } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let pin = self.get_mut(); pin.inner.poll_flush_unpin(cx) } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_close( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let pin = self.get_mut(); pin.inner.poll_close_unpin(cx) } @@ -143,7 +157,10 @@ pub struct OutboundDatagramImpl { impl OutboundDatagramImpl { #[allow(clippy::new_ret_no_self)] - pub fn new(udp: UdpSocket, resolver: ThreadSafeDNSResolver) -> AnyOutboundDatagram { + pub fn new( + udp: UdpSocket, + resolver: ThreadSafeDNSResolver, + ) -> AnyOutboundDatagram { let s = Self { inner: udp, resolver, @@ -157,7 +174,10 @@ impl OutboundDatagramImpl { impl Sink for OutboundDatagramImpl { type Error = io::Error; - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { if !self.flushed { match self.poll_flush(cx)? { Poll::Ready(()) => {} @@ -175,7 +195,10 @@ impl Sink for OutboundDatagramImpl { Ok(()) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { if self.flushed { return Poll::Ready(Ok(())); } @@ -233,7 +256,10 @@ impl Sink for OutboundDatagramImpl { } } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_close( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { ready!(self.poll_flush(cx))?; Poll::Ready(Ok(())) } @@ -241,7 +267,10 @@ impl Sink for OutboundDatagramImpl { impl Stream for OutboundDatagramImpl { type Item = UdpPacket; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let Self { ref mut inner, .. } = *self; let mut mem = vec![0u8; 65535]; let mut buf = ReadBuf::new(&mut mem); diff --git a/clash_lib/src/proxy/direct/mod.rs b/clash_lib/src/proxy/direct/mod.rs index de0fdf104..e230c7524 100644 --- a/clash_lib/src/proxy/direct/mod.rs +++ b/clash_lib/src/proxy/direct/mod.rs @@ -1,20 +1,25 @@ -use crate::app::dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, ChainedDatagramWrapper, - ChainedStream, ChainedStreamWrapper, +use crate::{ + app::{ + dispatcher::{ + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, + }, + dns::ThreadSafeDNSResolver, + }, + config::internal::proxy::PROXY_DIRECT, + proxy::{ + datagram::OutboundDatagramImpl, + utils::{new_tcp_stream, new_udp_socket}, + AnyOutboundHandler, OutboundHandler, + }, + session::Session, }; -use crate::app::dns::ThreadSafeDNSResolver; -use crate::config::internal::proxy::PROXY_DIRECT; -use crate::proxy::datagram::OutboundDatagramImpl; -use crate::proxy::utils::{new_tcp_stream, new_udp_socket}; -use crate::proxy::{AnyOutboundHandler, OutboundHandler}; -use crate::session::Session; use async_trait::async_trait; use serde::Serialize; use std::sync::Arc; -use super::utils::RemoteConnector; -use super::{ConnectorType, OutboundType}; +use super::{utils::RemoteConnector, ConnectorType, OutboundType}; #[derive(Serialize)] pub struct Handler; diff --git a/clash_lib/src/proxy/fallback/mod.rs b/clash_lib/src/proxy/fallback/mod.rs index fc87df675..d25992253 100644 --- a/clash_lib/src/proxy/fallback/mod.rs +++ b/clash_lib/src/proxy/fallback/mod.rs @@ -131,7 +131,8 @@ impl OutboundHandler for Handler { ); m.insert( "all".to_string(), - Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) as _, + Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) + as _, ); m } diff --git a/clash_lib/src/proxy/http/inbound/connector.rs b/clash_lib/src/proxy/http/inbound/connector.rs index cf227f962..6c6d0eaa7 100644 --- a/clash_lib/src/proxy/http/inbound/connector.rs +++ b/clash_lib/src/proxy/http/inbound/connector.rs @@ -1,14 +1,18 @@ -use crate::proxy::{AnyStream, ProxyError}; -use crate::session::{Network, Session, Type}; -use crate::Dispatcher; +use crate::{ + proxy::{AnyStream, ProxyError}, + session::{Network, Session, Type}, + Dispatcher, +}; use futures::FutureExt; use hyper::Uri; -use std::future::Future; -use std::net::SocketAddr; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; +use std::{ + future::Future, + net::SocketAddr, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; use tokio::io::duplex; use super::proxy::maybe_socks_addr; @@ -26,9 +30,10 @@ impl Connector { } impl tower::Service for Connector { - type Response = AnyStream; type Error = ProxyError; - type Future = Pin> + Send>>; + type Future = + Pin> + Send>>; + type Response = AnyStream; fn poll_ready( &mut self, @@ -50,7 +55,8 @@ impl tower::Service for Connector { network: Network::Tcp, typ: Type::Http, source: src, - destination: destination.ok_or(ProxyError::InvalidUrl(url.to_string()))?, + destination: destination + .ok_or(ProxyError::InvalidUrl(url.to_string()))?, ..Default::default() }; diff --git a/clash_lib/src/proxy/http/inbound/mod.rs b/clash_lib/src/proxy/http/inbound/mod.rs index 352926909..8c8cc5dcd 100644 --- a/clash_lib/src/proxy/http/inbound/mod.rs +++ b/clash_lib/src/proxy/http/inbound/mod.rs @@ -2,17 +2,16 @@ mod auth; mod connector; mod proxy; -use crate::common::auth::ThreadSafeAuthenticator; -use crate::proxy::utils::apply_tcp_options; -use crate::proxy::{AnyInboundListener, InboundListener}; -use crate::Dispatcher; +use crate::{ + common::auth::ThreadSafeAuthenticator, + proxy::{utils::apply_tcp_options, AnyInboundListener, InboundListener}, + Dispatcher, +}; use async_trait::async_trait; pub use proxy::handle as handle_http; -use std::io; -use std::net::SocketAddr; -use std::sync::Arc; +use std::{io, net::SocketAddr, sync::Arc}; use tokio::net::TcpListener; use tracing::warn; diff --git a/clash_lib/src/proxy/http/inbound/proxy.rs b/clash_lib/src/proxy/http/inbound/proxy.rs index 1438e2ed2..2e1be85e1 100644 --- a/clash_lib/src/proxy/http/inbound/proxy.rs +++ b/clash_lib/src/proxy/http/inbound/proxy.rs @@ -20,13 +20,13 @@ use crate::{ use super::{auth::authenticate_req, connector::Connector}; pub fn maybe_socks_addr(r: &Uri) -> Option { - let port = r - .port_u16() - .unwrap_or(match r.scheme().map(|s| s.as_str()).unwrap_or("http") { + let port = r.port_u16().unwrap_or( + match r.scheme().map(|s| s.as_str()).unwrap_or("http") { "http" => 80 as _, "https" => 443 as _, _ => return None, - }); + }, + ); r.host().map(|x| { if let Ok(ip) = x.parse::() { @@ -107,11 +107,9 @@ struct ProxyService { } impl Service> for ProxyService { - type Response = Response; - type Error = ProxyError; - type Future = BoxFuture<'static, Result>; + type Response = Response; fn poll_ready( &mut self, diff --git a/clash_lib/src/proxy/http/mod.rs b/clash_lib/src/proxy/http/mod.rs index b1813e951..f5b1d2e04 100644 --- a/clash_lib/src/proxy/http/mod.rs +++ b/clash_lib/src/proxy/http/mod.rs @@ -1,4 +1,3 @@ mod inbound; -pub use inbound::handle_http; -pub use inbound::Listener; +pub use inbound::{handle_http, Listener}; diff --git a/clash_lib/src/proxy/loadbalance/mod.rs b/clash_lib/src/proxy/loadbalance/mod.rs index 0e518aa26..61c19eb52 100644 --- a/clash_lib/src/proxy/loadbalance/mod.rs +++ b/clash_lib/src/proxy/loadbalance/mod.rs @@ -43,7 +43,10 @@ pub struct Handler { } impl Handler { - pub fn new(opts: HandlerOptions, providers: Vec) -> Self { + pub fn new( + opts: HandlerOptions, + providers: Vec, + ) -> Self { let strategy_fn = match opts.strategy { LoadBalanceStrategy::ConsistentHashing => strategy_consistent_hashring(), LoadBalanceStrategy::RoundRobin => strategy_rr(), @@ -135,7 +138,8 @@ impl OutboundHandler for Handler { m.insert( "all".to_string(), - Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) as _, + Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) + as _, ); m } diff --git a/clash_lib/src/proxy/mixed/mod.rs b/clash_lib/src/proxy/mixed/mod.rs index 371e162a4..ce5ae863e 100644 --- a/clash_lib/src/proxy/mixed/mod.rs +++ b/clash_lib/src/proxy/mixed/mod.rs @@ -1,16 +1,16 @@ -use crate::common::auth::ThreadSafeAuthenticator; -use crate::proxy::{AnyInboundListener, InboundListener}; -use crate::session::{Network, Session}; -use crate::Dispatcher; +use crate::{ + common::auth::ThreadSafeAuthenticator, + proxy::{AnyInboundListener, InboundListener}, + session::{Network, Session}, + Dispatcher, +}; use async_trait::async_trait; -use std::net::SocketAddr; -use std::sync::Arc; +use std::{net::SocketAddr, sync::Arc}; use tokio::net::TcpListener; use tracing::warn; -use super::utils::apply_tcp_options; -use super::{http, socks}; +use super::{http, socks, utils::apply_tcp_options}; pub struct Listener { addr: SocketAddr, @@ -76,13 +76,25 @@ impl InboundListener for Listener { }; tokio::spawn(async move { - socks::handle_tcp(&mut sess, &mut socket, dispatcher, authenticator).await + socks::handle_tcp( + &mut sess, + &mut socket, + dispatcher, + authenticator, + ) + .await }); } _ => { let src = socket.peer_addr()?; - http::handle_http(Box::new(socket), src, dispatcher, authenticator).await; + http::handle_http( + Box::new(socket), + src, + dispatcher, + authenticator, + ) + .await; } } } diff --git a/clash_lib/src/proxy/mocks.rs b/clash_lib/src/proxy/mocks.rs index 875f03f85..b26dd931a 100644 --- a/clash_lib/src/proxy/mocks.rs +++ b/clash_lib/src/proxy/mocks.rs @@ -8,7 +8,8 @@ use crate::{ dispatcher::{BoxedChainedDatagram, BoxedChainedStream}, dns::ThreadSafeDNSResolver, remote_content_manager::providers::{ - proxy_provider::ProxyProvider, Provider, ProviderType, ProviderVehicleType, + proxy_provider::ProxyProvider, Provider, ProviderType, + ProviderVehicleType, }, }, session::Session, diff --git a/clash_lib/src/proxy/mod.rs b/clash_lib/src/proxy/mod.rs index 16ec0ae41..ca9bb9a65 100644 --- a/clash_lib/src/proxy/mod.rs +++ b/clash_lib/src/proxy/mod.rs @@ -1,21 +1,25 @@ -use crate::app::dispatcher::{BoxedChainedDatagram, BoxedChainedStream}; -use crate::app::dns::ThreadSafeDNSResolver; -use crate::proxy::datagram::UdpPacket; -use crate::proxy::utils::Interface; -use crate::session::Session; +use crate::{ + app::{ + dispatcher::{BoxedChainedDatagram, BoxedChainedStream}, + dns::ThreadSafeDNSResolver, + }, + proxy::{datagram::UdpPacket, utils::Interface}, + session::Session, +}; use async_trait::async_trait; use erased_serde::Serialize as ESerialize; use futures::{Sink, Stream}; use serde::{Deserialize, Serialize}; use tracing::error; -use std::collections::HashMap; -use std::fmt::{Debug, Display}; -use std::io; -use std::sync::Arc; +use std::{ + collections::HashMap, + fmt::{Debug, Display}, + io, + sync::Arc, +}; -use tokio::io::AsyncRead; -use tokio::io::AsyncWrite; +use tokio::io::{AsyncRead, AsyncWrite}; use self::utils::RemoteConnector; @@ -65,7 +69,10 @@ pub enum ProxyError { } pub trait ProxyStream: AsyncRead + AsyncWrite + Send + Sync + Unpin + Debug {} -impl ProxyStream for T where T: AsyncRead + AsyncWrite + Send + Sync + Unpin + Debug {} +impl ProxyStream for T where + T: AsyncRead + AsyncWrite + Send + Sync + Unpin + Debug +{ +} pub type AnyStream = Box; pub trait InboundDatagram: diff --git a/clash_lib/src/proxy/reject/mod.rs b/clash_lib/src/proxy/reject/mod.rs index bb4b23978..4be4c700f 100644 --- a/clash_lib/src/proxy/reject/mod.rs +++ b/clash_lib/src/proxy/reject/mod.rs @@ -1,12 +1,15 @@ -use crate::app::dispatcher::{BoxedChainedDatagram, BoxedChainedStream}; -use crate::app::dns::ThreadSafeDNSResolver; -use crate::config::internal::proxy::PROXY_REJECT; -use crate::proxy::{AnyOutboundHandler, OutboundHandler}; -use crate::session::Session; +use crate::{ + app::{ + dispatcher::{BoxedChainedDatagram, BoxedChainedStream}, + dns::ThreadSafeDNSResolver, + }, + config::internal::proxy::PROXY_REJECT, + proxy::{AnyOutboundHandler, OutboundHandler}, + session::Session, +}; use async_trait::async_trait; use serde::Serialize; -use std::io; -use std::sync::Arc; +use std::{io, sync::Arc}; use super::{ConnectorType, OutboundType}; diff --git a/clash_lib/src/proxy/relay/mod.rs b/clash_lib/src/proxy/relay/mod.rs index a71eead94..19adbc27f 100644 --- a/clash_lib/src/proxy/relay/mod.rs +++ b/clash_lib/src/proxy/relay/mod.rs @@ -8,8 +8,8 @@ use tracing::debug; use crate::{ app::{ dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, ChainedDatagramWrapper, - ChainedStream, ChainedStreamWrapper, + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, @@ -20,8 +20,8 @@ use crate::{ use super::{ utils::{ - provider_helper::get_proxies_from_providers, DirectConnector, ProxyConnector, - RemoteConnector, + provider_helper::get_proxies_from_providers, DirectConnector, + ProxyConnector, RemoteConnector, }, AnyOutboundHandler, ConnectorType, OutboundHandler, OutboundType, }; @@ -80,16 +80,26 @@ impl OutboundHandler for Handler { proxy.connect_stream(sess, resolver).await } _ => { - let mut connector: Box = Box::new(DirectConnector::new()); + let mut connector: Box = + Box::new(DirectConnector::new()); let (proxies, last) = proxies.split_at(proxies.len() - 1); for proxy in proxies { - debug!("tcp relay `{}` via proxy `{}`", self.name(), proxy.name()); - connector = Box::new(ProxyConnector::new(proxy.clone(), connector)); + debug!( + "tcp relay `{}` via proxy `{}`", + self.name(), + proxy.name() + ); + connector = + Box::new(ProxyConnector::new(proxy.clone(), connector)); } debug!("relay `{}` via proxy `{}`", self.name(), last[0].name()); let s = last[0] - .connect_stream_with_connector(sess, resolver, connector.as_ref()) + .connect_stream_with_connector( + sess, + resolver, + connector.as_ref(), + ) .await?; let chained = ChainedStreamWrapper::new(s); @@ -115,14 +125,24 @@ impl OutboundHandler for Handler { proxy.connect_datagram(sess, resolver).await } _ => { - let mut connector: Box = Box::new(DirectConnector::new()); + let mut connector: Box = + Box::new(DirectConnector::new()); let (proxies, last) = proxies.split_at(proxies.len() - 1); for proxy in proxies { - debug!("udp relay `{}` via proxy `{}`", self.name(), proxy.name()); - connector = Box::new(ProxyConnector::new(proxy.clone(), connector)); + debug!( + "udp relay `{}` via proxy `{}`", + self.name(), + proxy.name() + ); + connector = + Box::new(ProxyConnector::new(proxy.clone(), connector)); } let d = last[0] - .connect_datagram_with_connector(sess, resolver, connector.as_ref()) + .connect_datagram_with_connector( + sess, + resolver, + connector.as_ref(), + ) .await?; let chained = ChainedDatagramWrapper::new(d); @@ -143,7 +163,8 @@ impl OutboundHandler for Handler { m.insert("type".to_string(), Box::new(self.proto()) as _); m.insert( "all".to_string(), - Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) as _, + Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) + as _, ); m @@ -155,11 +176,13 @@ mod tests { use tokio::sync::RwLock; - use crate::proxy::mocks::MockDummyProxyProvider; - use crate::proxy::utils::test_utils::Suite; - use crate::proxy::utils::test_utils::{consts::*, docker_runner::DockerTestRunner}; - use crate::proxy::utils::test_utils::{ - docker_runner::DockerTestRunnerBuilder, run_test_suites_and_cleanup, + use crate::proxy::{ + mocks::MockDummyProxyProvider, + utils::test_utils::{ + consts::*, + docker_runner::{DockerTestRunner, DockerTestRunnerBuilder}, + run_test_suites_and_cleanup, Suite, + }, }; use super::*; @@ -204,8 +227,14 @@ mod tests { proxies }); - let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); - run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::tcp_tests()).await + let handler = + Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); + run_test_suites_and_cleanup( + handler, + get_ss_runner(port).await?, + Suite::tcp_tests(), + ) + .await } #[tokio::test] @@ -236,7 +265,13 @@ mod tests { proxies }); - let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); - run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::tcp_tests()).await + let handler = + Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); + run_test_suites_and_cleanup( + handler, + get_ss_runner(port).await?, + Suite::tcp_tests(), + ) + .await } } diff --git a/clash_lib/src/proxy/selector/mod.rs b/clash_lib/src/proxy/selector/mod.rs index 0dca766fb..3d2164a06 100644 --- a/clash_lib/src/proxy/selector/mod.rs +++ b/clash_lib/src/proxy/selector/mod.rs @@ -74,7 +74,8 @@ impl Handler { } } debug!("selected proxy `{}` not found", current); - // in the case the selected proxy is not found(stale cache), return the first one + // in the case the selected proxy is not found(stale cache), return the + // first one proxies.first().unwrap().clone() } } @@ -182,7 +183,8 @@ impl OutboundHandler for Handler { m.insert("now".to_string(), Box::new(self.current().await) as _); m.insert( "all".to_string(), - Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) as _, + Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) + as _, ); m } @@ -224,7 +226,8 @@ mod tests { ) .await; - let selector_control = Arc::new(Mutex::new(handler.clone())) as ThreadSafeSelectorControl; + let selector_control = + Arc::new(Mutex::new(handler.clone())) as ThreadSafeSelectorControl; let outbound_handler = Arc::new(handler); assert_eq!( diff --git a/clash_lib/src/proxy/shadowsocks/datagram.rs b/clash_lib/src/proxy/shadowsocks/datagram.rs index 6fb03ebfd..0e0434788 100644 --- a/clash_lib/src/proxy/shadowsocks/datagram.rs +++ b/clash_lib/src/proxy/shadowsocks/datagram.rs @@ -47,7 +47,10 @@ impl OutboundDatagramShadowsocks { impl Sink for OutboundDatagramShadowsocks { type Error = io::Error; - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { if !self.flushed { match self.poll_flush(cx)? { Poll::Ready(()) => {} @@ -66,7 +69,10 @@ impl Sink for OutboundDatagramShadowsocks { } #[instrument(skip(self, cx))] - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { if self.flushed { return Poll::Ready(Ok(())); } @@ -113,7 +119,8 @@ impl Sink for OutboundDatagramShadowsocks { let n = ready!(inner.poll_send_to(dst, &addr, data, cx))?; debug!( - "send udp packet to remote ss server, len: {}, remote_addr: {}, dst_addr: {}", + "send udp packet to remote ss server, len: {}, remote_addr: {}, \ + dst_addr: {}", n, dst, addr ); @@ -139,7 +146,10 @@ impl Sink for OutboundDatagramShadowsocks { } } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_close( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { ready!(self.poll_flush(cx))?; Poll::Ready(Ok(())) } @@ -149,7 +159,10 @@ impl Stream for OutboundDatagramShadowsocks { type Item = UdpPacket; #[instrument(skip(self, cx))] - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let Self { ref mut buf, ref inner, @@ -162,7 +175,7 @@ impl Stream for OutboundDatagramShadowsocks { debug!("recv udp packet from remote ss server: {:?}", rv); match rv { - Ok((n, src, _, _)) => Poll::Ready(Some(UdpPacket { + Ok((n, src, ..)) => Poll::Ready(Some(UdpPacket { data: buf.filled()[..n].to_vec(), src_addr: src.into(), dst_addr: SocksAddr::any_ipv4(), diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index 76d76a73f..334d60dba 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -8,14 +8,15 @@ use async_trait::async_trait; use futures::TryFutureExt; use shadowsocks::{ config::ServerType, context::Context, crypto::CipherKind, - relay::udprelay::proxy_socket::UdpSocketType, ProxyClientStream, ProxySocket, ServerConfig, + relay::udprelay::proxy_socket::UdpSocketType, ProxyClientStream, ProxySocket, + ServerConfig, }; use crate::{ app::{ dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, ChainedDatagramWrapper, - ChainedStream, ChainedStreamWrapper, + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, }, @@ -46,7 +47,9 @@ pub struct SimpleOBFSOption { impl TryFrom> for SimpleOBFSOption { type Error = crate::Error; - fn try_from(value: HashMap) -> Result { + fn try_from( + value: HashMap, + ) -> Result { let host = value .get("host") .and_then(|x| x.as_str()) @@ -84,7 +87,9 @@ pub struct V2RayOBFSOption { impl TryFrom> for V2RayOBFSOption { type Error = crate::Error; - fn try_from(value: HashMap) -> Result { + fn try_from( + value: HashMap, + ) -> Result { let host = value .get("host") .and_then(|x| x.as_str()) @@ -95,7 +100,10 @@ impl TryFrom> for V2RayOBFSOption { .ok_or(Error::InvalidConfig("obfs mode is required".to_owned()))?; if mode != "websocket" { - return Err(Error::InvalidConfig(format!("invalid obfs mode: {}", mode))); + return Err(Error::InvalidConfig(format!( + "invalid obfs mode: {}", + mode + ))); } let path = value @@ -142,7 +150,9 @@ pub struct ShadowTlsOption { impl TryFrom> for ShadowTlsOption { type Error = crate::Error; - fn try_from(value: HashMap) -> Result { + fn try_from( + value: HashMap, + ) -> Result { let host = value .get("host") .and_then(|x| x.as_str()) @@ -200,14 +210,19 @@ impl Handler { let stream: AnyStream = match &self.opts.plugin_opts { Some(plugin) => match plugin { OBFSOption::Simple(opts) => { - tracing::warn!("simple-obfs is deprecated, please use v2ray-plugin instead"); + tracing::warn!( + "simple-obfs is deprecated, please use v2ray-plugin instead" + ); match opts.mode { - SimpleOBFSMode::Http => { - simple_obfs::SimpleObfsHTTP::new(s, opts.host.clone(), self.opts.port) - .into() - } + SimpleOBFSMode::Http => simple_obfs::SimpleObfsHTTP::new( + s, + opts.host.clone(), + self.opts.port, + ) + .into(), SimpleOBFSMode::Tls => { - simple_obfs::SimpleObfsTLS::new(s, opts.host.clone()).into() + simple_obfs::SimpleObfsTLS::new(s, opts.host.clone()) + .into() } } } @@ -231,7 +246,12 @@ impl Handler { "aes-128-gcm" => CipherKind::AES_128_GCM, "aes-256-gcm" => CipherKind::AES_256_GCM, "chacha20-ietf-poly1305" => CipherKind::CHACHA20_POLY1305, - _ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported cipher")), + _ => { + return Err(io::Error::new( + io::ErrorKind::Other, + "unsupported cipher", + )) + } }, ); @@ -303,7 +323,12 @@ impl OutboundHandler for Handler { "aes-128-gcm" => CipherKind::AES_128_GCM, "aes-256-gcm" => CipherKind::AES_256_GCM, "chacha20-ietf-poly1305" => CipherKind::CHACHA20_POLY1305, - _ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported cipher")), + _ => { + return Err(io::Error::new( + io::ErrorKind::Other, + "unsupported cipher", + )) + } }, ); let socket = new_udp_socket( @@ -314,7 +339,8 @@ impl OutboundHandler for Handler { ) .await?; - let socket = ProxySocket::from_socket(UdpSocketType::Client, ctx, &cfg, socket); + let socket = + ProxySocket::from_socket(UdpSocketType::Client, ctx, &cfg, socket); let d = OutboundDatagramShadowsocks::new( socket, (self.opts.server.to_owned(), self.opts.port), @@ -356,7 +382,9 @@ impl OutboundHandler for Handler { #[cfg(all(test, not(ci)))] mod tests { - use super::super::utils::test_utils::{consts::*, docker_runner::DockerTestRunner}; + use super::super::utils::test_utils::{ + consts::*, docker_runner::DockerTestRunner, + }; use crate::proxy::utils::test_utils::{ docker_runner::{DockerTestRunnerBuilder, MultiDockerTestRunner}, run_test_suites_and_cleanup, Suite, @@ -394,7 +422,12 @@ mod tests { }; let port = opts.port; let handler = Handler::new(opts); - run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::all()).await + run_test_suites_and_cleanup( + handler, + get_ss_runner(port).await?, + Suite::all(), + ) + .await } async fn get_shadowtls_runner( @@ -426,7 +459,8 @@ mod tests { async fn test_shadowtls() -> anyhow::Result<()> { // the real port that used for communication let shadow_tls_port = 10002; - // not important, you can assign any port that is not conflict with others + // not important, you can assign any port that is not conflict with + // others let ss_port = 10004; let opts = HandlerOptions { name: "test-ss".to_owned(), @@ -443,7 +477,8 @@ mod tests { udp: false, }; let handler: Arc = Handler::new(opts); - // we need to store all the runners in a container, to make sure all of them can be destroyed after the test + // we need to store all the runners in a container, to make sure all of + // them can be destroyed after the test let mut chained = MultiDockerTestRunner::default(); chained.add(get_ss_runner(ss_port)).await; chained diff --git a/clash_lib/src/proxy/shadowsocks/shadow_tls/connector.rs b/clash_lib/src/proxy/shadowsocks/shadow_tls/connector.rs index aa4ffeca4..7d9fc71f7 100644 --- a/clash_lib/src/proxy/shadowsocks/shadow_tls/connector.rs +++ b/clash_lib/src/proxy/shadowsocks/shadow_tls/connector.rs @@ -1,21 +1,19 @@ -use std::io; -use std::ptr::copy_nonoverlapping; -use std::sync::Arc; +use std::{io, ptr::copy_nonoverlapping, sync::Arc}; use rand::Rng; use rand::distributions::Distribution; use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use tokio_rustls::client::TlsStream; -use tokio_rustls::TlsConnector; +use tokio_rustls::{client::TlsStream, TlsConnector}; -use crate::proxy::shadowsocks::ShadowTlsOption; -use crate::proxy::AnyStream; +use crate::proxy::{shadowsocks::ShadowTlsOption, AnyStream}; use super::prelude::*; -use super::stream::{ProxyTlsStream, VerifiedStream}; -use super::utils::Hmac; +use super::{ + stream::{ProxyTlsStream, VerifiedStream}, + utils::Hmac, +}; #[derive(Clone, Debug)] #[allow(unused)] @@ -39,13 +37,17 @@ impl Connector { TlsConnector::from(Arc::new(tls_config.clone())) } - pub async fn wrap(opts: &ShadowTlsOption, stream: AnyStream) -> std::io::Result { + pub async fn wrap( + opts: &ShadowTlsOption, + stream: AnyStream, + ) -> std::io::Result { let proxy_stream = ProxyTlsStream::new(stream, &opts.password); let hamc_handshake = Hmac::new(&opts.password, (&[], &[])); let sni_name = rustls::ServerName::try_from(opts.host.as_str()) .unwrap_or_else(|_| panic!("invalid server name: {}", opts.host)); - let session_id_generator = move |data: &_| generate_session_id(&hamc_handshake, data); + let session_id_generator = + move |data: &_| generate_session_id(&hamc_handshake, data); let connector = Self::connector(); let mut tls = connector .connect_with(sni_name, proxy_stream, Some(session_id_generator), |_| {}) @@ -58,16 +60,21 @@ impl Connector { .as_ref() .map(|s| (s.server_random, s.hmac.to_owned())); - // whatever the fake_request is successful or not, we should return an error when strict mode is enabled + // whatever the fake_request is successful or not, we should return an + // error when strict mode is enabled if (!authorized || maybe_server_random_and_hamc.is_none()) && opts.strict { - tracing::warn!("shadow-tls V3 strict enabled: traffic hijacked or TLS1.3 is not supported, perform fake request"); + tracing::warn!( + "shadow-tls V3 strict enabled: traffic hijacked or TLS1.3 is not \ + supported, perform fake request" + ); tls.get_mut().0.fake_request = true; fake_request(tls).await?; return Err(io::Error::new( io::ErrorKind::Other, - "V3 strict enabled: traffic hijacked or TLS1.3 is not supported, fake request" + "V3 strict enabled: traffic hijacked or TLS1.3 is not supported, \ + fake request" .to_string(), )); } @@ -77,14 +84,17 @@ impl Connector { None => { return Err(io::Error::new( io::ErrorKind::Other, - "server random and hmac not extracted from handshake, fail to connect" + "server random and hmac not extracted from handshake, fail to \ + connect" .to_string(), )); } }; - let hmac_client = Hmac::new(&opts.password, (&server_random, "C".as_bytes())); - let hmac_server = Hmac::new(&opts.password, (&server_random, "S".as_bytes())); + let hmac_client = + Hmac::new(&opts.password, (&server_random, "C".as_bytes())); + let hmac_server = + Hmac::new(&opts.password, (&server_random, "S".as_bytes())); let verified_stream = VerifiedStream::new( tls.into_inner().0.raw, @@ -134,8 +144,8 @@ async fn fake_request( ) -> std::io::Result<()> { const HEADER: &[u8; 207] = b"GET / HTTP/1.1\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36\nAccept: gzip, deflate, br\nConnection: Close\nCookie: sessionid="; const FAKE_REQUEST_LENGTH_RANGE: (usize, usize) = (16, 64); - let cnt = - rand::thread_rng().gen_range(FAKE_REQUEST_LENGTH_RANGE.0..FAKE_REQUEST_LENGTH_RANGE.1); + let cnt = rand::thread_rng() + .gen_range(FAKE_REQUEST_LENGTH_RANGE.0..FAKE_REQUEST_LENGTH_RANGE.1); let mut buffer = Vec::with_capacity(cnt + HEADER.len() + 1); buffer.extend_from_slice(HEADER); diff --git a/clash_lib/src/proxy/shadowsocks/shadow_tls/stream.rs b/clash_lib/src/proxy/shadowsocks/shadow_tls/stream.rs index f01cab935..9f441339c 100644 --- a/clash_lib/src/proxy/shadowsocks/shadow_tls/stream.rs +++ b/clash_lib/src/proxy/shadowsocks/shadow_tls/stream.rs @@ -54,9 +54,11 @@ impl ReadExt for T { unsafe { read_buf.set_len(size) } loop { if *read_pos < size { - // # safety: read_pos]) + &mut *((&mut read_buf[*read_pos..size]) as *mut _ + as *mut [MaybeUninit]) }; let mut buf = ReadBuf::uninit(dst); let ptr = buf.filled().as_ptr(); @@ -98,7 +100,8 @@ pub struct ProxyTlsStream { read_authorized: bool, tls13: bool, - // if true, the stream will only act as a wrapper, and won't modify the inner byte stream + // if true, the stream will only act as a wrapper, and won't modify the + // inner byte stream pub fake_request: bool, } @@ -189,7 +192,8 @@ impl AsyncRead for ProxyTlsStream { TLS_RANDOM_SIZE, ) } - let hmac = Hmac::new(&this.password, (&server_random, &[])); + let hmac = + Hmac::new(&this.password, (&server_random, &[])); let key = kdf(&this.password, &server_random); this.certs = Some(Certs { server_random, @@ -202,7 +206,9 @@ impl AsyncRead for ProxyTlsStream { APPLICATION_DATA => { this.read_authorized = false; if body.len() > HMAC_SIZE { - if let Some(Certs { hmac, key, .. }) = this.certs.as_mut() { + if let Some(Certs { hmac, key, .. }) = + this.certs.as_mut() + { hmac.update(&body[HMAC_SIZE..]); if hmac.finalize() == body[0..HMAC_SIZE] { // 1. xor to the the original data @@ -217,16 +223,22 @@ impl AsyncRead for ProxyTlsStream { }; // 3. rewrite the data size in the header (&mut header[3..5]) - .write_u16::(size as u16 - HMAC_SIZE as u16) + .write_u16::( + size as u16 - HMAC_SIZE as u16, + ) .unwrap(); this.read_authorized = true; - // 4. rewrite the body length to be put into the read buf + // 4. rewrite the body length to be put into + // the read buf unsafe { body.set_len(body.len() - HMAC_SIZE); } - // 4. put the header and body into our own read buf + // 4. put the header and body into our + // own read buf } else { - tracing::debug!("shadowtls verification failed"); + tracing::debug!( + "shadowtls verification failed" + ); } } } @@ -369,7 +381,12 @@ impl AsyncRead for VerifiedStream { // the application data from the data server // we need to verfiy and removec the hmac(4 bytes) - if verify_appdata(&header, &mut data, &mut this.server_cert, true) { + if verify_appdata( + &header, + &mut data, + &mut this.server_cert, + true, + ) { // modify data, reuse the read buf this.read_buf.clear(); this.read_buf.put(&data[HMAC_SIZE..]); @@ -438,7 +455,8 @@ impl AsyncWrite for VerifiedStream { }; this.write_buf.put_slice(&header_body); - this.write_state = WriteState::FlushingData(buf.len(), header_body.len(), 0); + this.write_state = + WriteState::FlushingData(buf.len(), header_body.len(), 0); } WriteState::FlushingData(consume, total, written) => { let nw = ready!(tokio_util::io::poll_write_buf( @@ -461,7 +479,8 @@ impl AsyncWrite for VerifiedStream { return Poll::Ready(Ok(consume)); } - this.write_state = WriteState::FlushingData(consume, total, written + nw); + this.write_state = + WriteState::FlushingData(consume, total, written + nw); } } } diff --git a/clash_lib/src/proxy/shadowsocks/shadow_tls/utils.rs b/clash_lib/src/proxy/shadowsocks/shadow_tls/utils.rs index f3bd1406e..3181f3bb1 100644 --- a/clash_lib/src/proxy/shadowsocks/shadow_tls/utils.rs +++ b/clash_lib/src/proxy/shadowsocks/shadow_tls/utils.rs @@ -20,7 +20,8 @@ pub(crate) mod prelude { pub(crate) const APPLICATION_DATA: u8 = 0x17; pub(crate) const SERVER_RANDOM_OFFSET: usize = 1 + 3 + 2; - pub(crate) const SESSION_ID_LEN_IDX: usize = TLS_HEADER_SIZE + 1 + 3 + 2 + TLS_RANDOM_SIZE; + pub(crate) const SESSION_ID_LEN_IDX: usize = + TLS_HEADER_SIZE + 1 + 3 + 2 + TLS_RANDOM_SIZE; pub(crate) const TLS_HMAC_HEADER_SIZE: usize = TLS_HEADER_SIZE + HMAC_SIZE; pub(crate) const COPY_BUF_SIZE: usize = 4096; @@ -41,7 +42,8 @@ impl Hmac { pub(crate) fn new(password: &str, init_data: (&[u8], &[u8])) -> Self { // Note: infact new_from_slice never returns Err. let mut hmac: hmac::Hmac = - hmac::Hmac::new_from_slice(password.as_bytes()).expect("unable to build hmac instance"); + hmac::Hmac::new_from_slice(password.as_bytes()) + .expect("unable to build hmac instance"); hmac.update(init_data.0); hmac.update(init_data.1); Self(hmac) @@ -58,7 +60,13 @@ impl Hmac { let hmac = self.0.clone(); let hash = hmac.finalize().into_bytes(); let mut res = [0; HMAC_SIZE]; - unsafe { copy_nonoverlapping(hash.as_slice().as_ptr(), res.as_mut_ptr(), HMAC_SIZE) }; + unsafe { + copy_nonoverlapping( + hash.as_slice().as_ptr(), + res.as_mut_ptr(), + HMAC_SIZE, + ) + }; res } @@ -134,7 +142,7 @@ pub(crate) fn support_tls13(frame: &[u8]) -> bool { } let mut cursor = std::io::Cursor::new(&frame[SESSION_ID_LEN_IDX..]); macro_rules! read_ok { - ($res: expr) => { + ($res:expr) => { match $res { Ok(r) => r, Err(_) => { diff --git a/clash_lib/src/proxy/shadowsocks/simple_obfs/http.rs b/clash_lib/src/proxy/shadowsocks/simple_obfs/http.rs index 3e4366fd2..dc09649ab 100644 --- a/clash_lib/src/proxy/shadowsocks/simple_obfs/http.rs +++ b/clash_lib/src/proxy/shadowsocks/simple_obfs/http.rs @@ -55,7 +55,8 @@ impl AsyncWrite for HTTPObfs { ) .as_bytes(), ); - buffer.put_slice(format!("Content-Length: {}\r\n", buf.len()).as_bytes()); + buffer + .put_slice(format!("Content-Length: {}\r\n", buf.len()).as_bytes()); buffer.put_slice(b"\r\n"); buffer.put_slice(buf); @@ -90,7 +91,8 @@ impl AsyncRead for HTTPObfs { buf: &mut tokio::io::ReadBuf<'_>, ) -> std::task::Poll> { let pin = self.get_mut(); - // as long as the buffer is not empty, we should return the data in the buffer first + // as long as the buffer is not empty, we should return the data in the + // buffer first if !pin.read_buf.is_empty() { let to_read = std::cmp::min(buf.remaining(), pin.read_buf.len()); if to_read == 0 { @@ -105,7 +107,8 @@ impl AsyncRead for HTTPObfs { if pin.first_response { // TODO: move this static buffer size to global constant - // maximum packet size of vmess/shadowsocks is about 16 KiB so define a buffer of 20 KiB to reduce the memory of each TCP relay + // maximum packet size of vmess/shadowsocks is about 16 KiB so + // define a buffer of 20 KiB to reduce the memory of each TCP relay let mut b = [0; 20 * 1024]; let mut b = tokio::io::ReadBuf::new(&mut b); match Pin::new(&mut pin.inner).poll_read(cx, &mut b) { diff --git a/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs b/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs index 2b6ffa319..12c80d554 100644 --- a/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs +++ b/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs @@ -10,8 +10,7 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::BufMut; use chrono::Utc; use futures::pin_mut; -use std::future::Future; -use std::task::ready; +use std::{future::Future, task::ready}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, ReadBuf}; use crate::proxy::AnyStream; @@ -68,7 +67,10 @@ impl AsyncWrite for TLSObfs { } } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = self.get_mut(); Pin::new(&mut this.inner).poll_flush(cx) } @@ -226,10 +228,11 @@ fn make_client_hello_msg<'a>(data: &[u8], server: &str) -> Cow<'a, Vec> { // cipher suites buf.put_slice(&[0x00, 0x38]); buf.put_slice(&[ - 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, - 0x2f, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, - 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, - 0x9c, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, + 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, + 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, + 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, + 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, ]); // compression @@ -259,14 +262,15 @@ fn make_client_hello_msg<'a>(data: &[u8], server: &str) -> Cow<'a, Vec> { // groups buf.put_slice(&[ - 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18, + 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, + 0x00, 0x18, ]); // signature buf.put_slice(&[ - 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, - 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, + 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, + 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, + 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, ]); // encrypt then mac diff --git a/clash_lib/src/proxy/socks/inbound/datagram.rs b/clash_lib/src/proxy/socks/inbound/datagram.rs index 5082849f9..1e45427e3 100644 --- a/clash_lib/src/proxy/socks/inbound/datagram.rs +++ b/clash_lib/src/proxy/socks/inbound/datagram.rs @@ -3,31 +3,33 @@ use bytes::{Buf, BufMut, Bytes, BytesMut}; use std::io; use tokio_util::codec::{Decoder, Encoder}; -/* -+----+------+------+----------+----------+----------+ -|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | -+----+------+------+----------+----------+----------+ -| 2 | 1 | 1 | Variable | 2 | Variable | -+----+------+------+----------+----------+----------+ - -The fields in the UDP request header are: - -o RSV Reserved X'0000' -o FRAG Current fragment number -o ATYP address type of following addresses: -o IP V4 address: X'01' -o DOMAINNAME: X'03' -o IP V6 address: X'04' -o DST.ADDR desired destination address -o DST.PORT desired destination port -o DATA user data -*/ +// +----+------+------+----------+----------+----------+ +// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +// +----+------+------+----------+----------+----------+ +// | 2 | 1 | 1 | Variable | 2 | Variable | +// +----+------+------+----------+----------+----------+ +// +// The fields in the UDP request header are: +// +// o RSV Reserved X'0000' +// o FRAG Current fragment number +// o ATYP address type of following addresses: +// o IP V4 address: X'01' +// o DOMAINNAME: X'03' +// o IP V6 address: X'04' +// o DST.ADDR desired destination address +// o DST.PORT desired destination port +// o DATA user data pub struct Socks5UDPCodec; impl Encoder<(Bytes, SocksAddr)> for Socks5UDPCodec { type Error = std::io::Error; - fn encode(&mut self, item: (Bytes, SocksAddr), dst: &mut BytesMut) -> Result<(), Self::Error> { + fn encode( + &mut self, + item: (Bytes, SocksAddr), + dst: &mut BytesMut, + ) -> Result<(), Self::Error> { dst.reserve(3 + item.1.size() + item.0.len()); dst.put_slice(&[0x0, 0x0, 0x0]); item.1.write_buf(dst); @@ -38,10 +40,13 @@ impl Encoder<(Bytes, SocksAddr)> for Socks5UDPCodec { } impl Decoder for Socks5UDPCodec { - type Item = (SocksAddr, BytesMut); type Error = std::io::Error; + type Item = (SocksAddr, BytesMut); - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + fn decode( + &mut self, + src: &mut BytesMut, + ) -> Result, Self::Error> { if src.len() < 3 { return Ok(None); } diff --git a/clash_lib/src/proxy/socks/inbound/mod.rs b/clash_lib/src/proxy/socks/inbound/mod.rs index e6a4f8bc3..3e99a2f83 100644 --- a/clash_lib/src/proxy/socks/inbound/mod.rs +++ b/clash_lib/src/proxy/socks/inbound/mod.rs @@ -1,14 +1,14 @@ mod datagram; mod stream; -use crate::common::auth::ThreadSafeAuthenticator; -use crate::proxy::utils::apply_tcp_options; -use crate::proxy::{AnyInboundListener, InboundListener}; -use crate::session::{Network, Session, Type}; -use crate::Dispatcher; +use crate::{ + common::auth::ThreadSafeAuthenticator, + proxy::{utils::apply_tcp_options, AnyInboundListener, InboundListener}, + session::{Network, Session, Type}, + Dispatcher, +}; use async_trait::async_trait; -use std::net::SocketAddr; -use std::sync::Arc; +use std::{net::SocketAddr, sync::Arc}; pub use stream::handle_tcp; use tokio::net::TcpListener; use tracing::warn; diff --git a/clash_lib/src/proxy/socks/inbound/stream.rs b/clash_lib/src/proxy/socks/inbound/stream.rs index 2597f01ac..fec380b63 100644 --- a/clash_lib/src/proxy/socks/inbound/stream.rs +++ b/clash_lib/src/proxy/socks/inbound/stream.rs @@ -1,18 +1,23 @@ -use crate::common::auth::ThreadSafeAuthenticator; -use crate::common::errors::new_io_error; -use crate::proxy::datagram::InboundUdp; -use crate::proxy::socks::inbound::datagram::Socks5UDPCodec; -use crate::proxy::socks::inbound::{auth_methods, response_code, socks_command, SOCKS5_VERSION}; -use crate::proxy::utils::new_udp_socket; -use crate::session::{Network, Session, SocksAddr, Type}; -use crate::Dispatcher; +use crate::{ + common::{auth::ThreadSafeAuthenticator, errors::new_io_error}, + proxy::{ + datagram::InboundUdp, + socks::inbound::{ + auth_methods, datagram::Socks5UDPCodec, response_code, socks_command, + SOCKS5_VERSION, + }, + utils::new_udp_socket, + }, + session::{Network, Session, SocksAddr, Type}, + Dispatcher, +}; use bytes::{BufMut, BytesMut}; -use std::net::SocketAddr; -use std::sync::Arc; -use std::{io, str}; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::TcpStream; +use std::{io, net::SocketAddr, str, sync::Arc}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; use tokio_util::udp::UdpFramed; use tracing::{instrument, trace, warn}; @@ -38,7 +43,10 @@ pub async fn handle_tcp<'a>( let n_methods = buf[1] as usize; if n_methods == 0 { - return Err(io::Error::new(io::ErrorKind::Other, "malformed SOCKS data")); + return Err(io::Error::new( + io::ErrorKind::Other, + "malformed SOCKS data", + )); } buf.resize(n_methods, 0); @@ -58,34 +66,34 @@ pub async fn handle_tcp<'a>( response[1] = auth_methods::USER_PASS; s.write_all(&response).await?; - /* - +----+------+----------+------+----------+ - |VER | ULEN | UNAME | PLEN | PASSWD | - +----+------+----------+------+----------+ - | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - +----+------+----------+------+----------+ - */ + // +----+------+----------+------+----------+ + // |VER | ULEN | UNAME | PLEN | PASSWD | + // +----+------+----------+------+----------+ + // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + // +----+------+----------+------+----------+ buf.resize(2, 0); s.read_exact(&mut buf[..]).await?; let ulen = buf[1] as usize; buf.resize(ulen, 0); s.read_exact(&mut buf[..]).await?; - let user = unsafe { str::from_utf8_unchecked(buf.to_owned().as_ref()).to_owned() }; + let user = unsafe { + str::from_utf8_unchecked(buf.to_owned().as_ref()).to_owned() + }; s.read_exact(&mut buf[..1]).await?; let plen = buf[0] as usize; buf.resize(plen, 0); s.read_exact(&mut buf[..]).await?; - let pass = unsafe { str::from_utf8_unchecked(buf.to_owned().as_ref()).to_owned() }; + let pass = unsafe { + str::from_utf8_unchecked(buf.to_owned().as_ref()).to_owned() + }; match authenticator.authenticate(&user, &pass) { - /* - +----+--------+ - |VER | STATUS | - +----+--------+ - | 1 | 1 | - +----+--------+ - */ + // +----+--------+ + // |VER | STATUS | + // +----+--------+ + // | 1 | 1 | + // +----+--------+ true => { response = [0x1, response_code::SUCCEEDED]; s.write_all(&response).await?; @@ -94,7 +102,10 @@ pub async fn handle_tcp<'a>( response = [0x1, response_code::FAILURE]; s.write_all(&response).await?; s.shutdown().await?; - return Err(io::Error::new(io::ErrorKind::Other, "auth failure")); + return Err(io::Error::new( + io::ErrorKind::Other, + "auth failure", + )); } } } else if methods.contains(&auth_methods::NO_AUTH) { @@ -174,8 +185,8 @@ pub async fn handle_tcp<'a>( let dispatcher_cloned = dispatcher.clone(); tokio::spawn(async move { - let handle = - dispatcher_cloned.dispatch_datagram(sess, Box::new(InboundUdp::new(framed))); + let handle = dispatcher_cloned + .dispatch_datagram(sess, Box::new(InboundUdp::new(framed))); close_listener.await.ok(); handle.send(0).ok(); }); diff --git a/clash_lib/src/proxy/socks/mod.rs b/clash_lib/src/proxy/socks/mod.rs index 3b185e1d0..3010d771d 100644 --- a/clash_lib/src/proxy/socks/mod.rs +++ b/clash_lib/src/proxy/socks/mod.rs @@ -1,6 +1,3 @@ mod inbound; -pub use inbound::handle_tcp; -pub use inbound::Listener; -pub use inbound::Socks5UDPCodec; -pub use inbound::SOCKS5_VERSION; +pub use inbound::{handle_tcp, Listener, Socks5UDPCodec, SOCKS5_VERSION}; diff --git a/clash_lib/src/proxy/tor/mod.rs b/clash_lib/src/proxy/tor/mod.rs index e0dfb96e0..54b68d2d2 100644 --- a/clash_lib/src/proxy/tor/mod.rs +++ b/clash_lib/src/proxy/tor/mod.rs @@ -8,7 +8,8 @@ use async_trait::async_trait; use crate::{ app::{ dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedStream, ChainedStreamWrapper, + BoxedChainedDatagram, BoxedChainedStream, ChainedStream, + ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, }, @@ -69,7 +70,9 @@ impl OutboundHandler for Handler { #[cfg(feature = "onion")] StreamPrefs::new() .any_exit_country() - .connect_to_onion_services(arti_client::config::BoolOrAuto::Explicit(true)), + .connect_to_onion_services( + arti_client::config::BoolOrAuto::Explicit(true), + ), #[cfg(not(feature = "onion"))] StreamPrefs::new().any_exit_country(), ) diff --git a/clash_lib/src/proxy/tor/stream.rs b/clash_lib/src/proxy/tor/stream.rs index 46ea1842e..93d3ecfd6 100644 --- a/clash_lib/src/proxy/tor/stream.rs +++ b/clash_lib/src/proxy/tor/stream.rs @@ -44,14 +44,20 @@ impl AsyncWrite for StreamWrapper { } } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { match self.0.try_lock() { Ok(mut stream) => Pin::new(&mut *stream).poll_flush(cx), Err(_) => Poll::Pending, } } - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_shutdown( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { match self.0.try_lock() { Ok(mut stream) => Pin::new(&mut *stream).poll_shutdown(cx), Err(_) => Poll::Pending, diff --git a/clash_lib/src/proxy/transport/grpc.rs b/clash_lib/src/proxy/transport/grpc.rs index e7dbe59b3..87cf50897 100644 --- a/clash_lib/src/proxy/transport/grpc.rs +++ b/clash_lib/src/proxy/transport/grpc.rs @@ -1,23 +1,22 @@ -use crate::common::errors::map_io_error; -use crate::proxy::AnyStream; +use crate::{common::errors::map_io_error, proxy::AnyStream}; -use bytes::Buf; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures::ready; use h2::{RecvStream, SendStream}; use http::{Request, Uri, Version}; -use prost::encoding::decode_varint; -use prost::encoding::encode_varint; +use prost::encoding::{decode_varint, encode_varint}; use tokio::sync::{mpsc, Mutex}; use tracing::warn; -use std::fmt::Debug; -use std::io; -use std::io::{Error, ErrorKind}; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; +use std::{ + fmt::Debug, + io, + io::{Error, ErrorKind}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; use tokio::io::{AsyncRead, AsyncWrite}; #[derive(Clone)] @@ -61,10 +60,11 @@ impl GrpcStreamBuilder { let mut client = client.ready().await.map_err(map_io_error)?; let req = self.req()?; - let (resp, send_stream) = client.send_request(req, false).map_err(map_io_error)?; + let (resp, send_stream) = + client.send_request(req, false).map_err(map_io_error)?; tokio::spawn(async move { if let Err(e) = h2.await { - //TODO: collect this somewhere? + // TODO: collect this somewhere? warn!("http2 got err:{:?}", e); } }); @@ -148,8 +148,9 @@ impl GrpcStream { let grpc_payload_len = (protobuf_header.len() + data.len()) as u32; grpc_header[1..5].copy_from_slice(&grpc_payload_len.to_be_bytes()); - let mut buf = - BytesMut::with_capacity(grpc_header.len() + protobuf_header.len() + data.len()); + let mut buf = BytesMut::with_capacity( + grpc_header.len() + protobuf_header.len() + data.len(), + ); buf.put_slice(&grpc_header[..]); buf.put_slice(&protobuf_header.freeze()[..]); buf.put_slice(data); @@ -182,7 +183,8 @@ impl AsyncRead for GrpcStream { { if self.payload_len == 0 { self.buffer.advance(6); - let payload_len = decode_varint(&mut self.buffer).map_err(map_io_error)?; + let payload_len = + decode_varint(&mut self.buffer).map_err(map_io_error)?; self.payload_len = payload_len as usize; } @@ -210,7 +212,8 @@ impl AsyncRead for GrpcStream { while self.payload_len > 0 || self.buffer.len() > 6 { if self.payload_len == 0 { self.buffer.advance(6); - let payload_len = decode_varint(&mut self.buffer).map_err(map_io_error)?; + let payload_len = + decode_varint(&mut self.buffer).map_err(map_io_error)?; self.payload_len = payload_len as usize; } let to_read = std::cmp::min(self.buffer.len(), self.payload_len); @@ -228,7 +231,12 @@ impl AsyncRead for GrpcStream { .flow_control() .release_capacity(b.len()) .map_or_else( - |e| Poll::Ready(Err(Error::new(ErrorKind::ConnectionReset, e))), + |e| { + Poll::Ready(Err(Error::new( + ErrorKind::ConnectionReset, + e, + ))) + }, |_| Poll::Ready(Ok(())), ) } @@ -272,12 +280,18 @@ impl AsyncWrite for GrpcStream { } #[inline] - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + fn poll_flush( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(())) } #[inline] - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { self.send.send_reset(h2::Reason::NO_ERROR); self.send .poll_reset(cx) diff --git a/clash_lib/src/proxy/transport/h2.rs b/clash_lib/src/proxy/transport/h2.rs index 753299a8b..bcc2d0b78 100644 --- a/clash_lib/src/proxy/transport/h2.rs +++ b/clash_lib/src/proxy/transport/h2.rs @@ -27,7 +27,9 @@ impl Http2Config { .authority(self.hosts[uri_idx].as_str()) .path_and_query(self.path.clone()) .build() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? + .map_err(|e| { + std::io::Error::new(std::io::ErrorKind::InvalidData, e) + })? }; let mut request = Request::builder() .uri(uri) @@ -42,10 +44,15 @@ impl Http2Config { Ok(request.body(()).expect("build req")) } - pub async fn proxy_stream(&self, stream: AnyStream) -> std::io::Result { - let (mut client, h2) = h2::client::handshake(stream).await.map_err(map_io_error)?; + pub async fn proxy_stream( + &self, + stream: AnyStream, + ) -> std::io::Result { + let (mut client, h2) = + h2::client::handshake(stream).await.map_err(map_io_error)?; let req = self.req()?; - let (resp, send_stream) = client.send_request(req, false).map_err(map_io_error)?; + let (resp, send_stream) = + client.send_request(req, false).map_err(map_io_error)?; tokio::spawn(async move { if let Err(e) = h2.await { error!("h2 error: {}", e); @@ -107,7 +114,12 @@ impl AsyncRead for Http2Stream { .flow_control() .release_capacity(to_read) .map_or_else( - |e| Err(std::io::Error::new(std::io::ErrorKind::ConnectionReset, e)), + |e| { + Err(std::io::Error::new( + std::io::ErrorKind::ConnectionReset, + e, + )) + }, |_| Ok(()), ) } diff --git a/clash_lib/src/proxy/transport/ws/mod.rs b/clash_lib/src/proxy/transport/ws/mod.rs index 4eaef9f55..7af6ae5b6 100644 --- a/clash_lib/src/proxy/transport/ws/mod.rs +++ b/clash_lib/src/proxy/transport/ws/mod.rs @@ -62,7 +62,10 @@ impl WebsocketStreamBuilder { request.body(()).unwrap() } - pub async fn proxy_stream(&self, stream: AnyStream) -> std::io::Result { + pub async fn proxy_stream( + &self, + stream: AnyStream, + ) -> std::io::Result { let req = self.req(); if self.max_early_data > 0 { let early_data_conn = WebsocketEarlyDataConn::new( @@ -74,9 +77,10 @@ impl WebsocketStreamBuilder { ); Ok(Box::new(early_data_conn)) } else { - let (stream, resp) = client_async_with_config(req, stream, self.ws_config) - .await - .map_err(map_io_error)?; + let (stream, resp) = + client_async_with_config(req, stream, self.ws_config) + .await + .map_err(map_io_error)?; if resp.status() != StatusCode::SWITCHING_PROTOCOLS { return Err(std::io::Error::new( diff --git a/clash_lib/src/proxy/transport/ws/websocket_early_data.rs b/clash_lib/src/proxy/transport/ws/websocket_early_data.rs index ff469b0c3..2c86f05bc 100644 --- a/clash_lib/src/proxy/transport/ws/websocket_early_data.rs +++ b/clash_lib/src/proxy/transport/ws/websocket_early_data.rs @@ -6,11 +6,12 @@ use std::{ }; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use futures::ready; -use futures::Future; +use futures::{ready, Future}; use http::{HeaderValue, Request, StatusCode}; use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_tungstenite::{client_async_with_config, tungstenite::protocol::WebSocketConfig}; +use tokio_tungstenite::{ + client_async_with_config, tungstenite::protocol::WebSocketConfig, +}; use crate::{ common::errors::{map_io_error, new_io_error}, @@ -23,7 +24,13 @@ pub struct WebsocketEarlyDataConn { stream: Option, req: Option>, stream_future: Option< - Pin> + Send + Sync>>, + Pin< + Box< + dyn std::future::Future> + + Send + + Sync, + >, + >, >, early_waker: Option, flush_waker: Option, @@ -73,7 +80,13 @@ impl WebsocketEarlyDataConn { stream: AnyStream, req: Request<()>, config: Option, - ) -> Pin> + Send + Sync>> { + ) -> Pin< + Box< + dyn std::future::Future> + + Send + + Sync, + >, + > { async fn run( stream: AnyStream, req: Request<()>, @@ -83,7 +96,9 @@ impl WebsocketEarlyDataConn { .await .map_err(map_io_error)?; if resp.status() != StatusCode::SWITCHING_PROTOCOLS { - return Err(new_io_error("msg: websocket early data handshake failed")); + return Err(new_io_error( + "msg: websocket early data handshake failed", + )); } let rv = Box::new(WebsocketConn::from_websocket(stream)); Ok(rv) @@ -135,21 +150,25 @@ impl AsyncWrite for WebsocketEarlyDataConn { } return Poll::Ready(Ok(self.as_mut().early_data_len)); } else { - let mut req = self.as_mut().req.take().expect("req must be present"); + let mut req = + self.as_mut().req.take().expect("req must be present"); if let Some(v) = req .headers_mut() .get_mut(&self.as_mut().early_data_header_name) { self.as_mut().early_data_len = cmp::min(self.as_mut().early_data_len, buf.len()); - let header_value = - URL_SAFE_NO_PAD.encode(&buf[..self.as_mut().early_data_len]); - *v = HeaderValue::from_str(&header_value).expect("bad header value"); + let header_value = URL_SAFE_NO_PAD + .encode(&buf[..self.as_mut().early_data_len]); + *v = HeaderValue::from_str(&header_value) + .expect("bad header value"); } - let stream = self.as_mut().stream.take().expect("msg: bad state"); + let stream = + self.as_mut().stream.take().expect("msg: bad state"); let config = self.as_mut().ws_config.take(); - self.as_mut().stream_future = Some(Self::proxy_stream(stream, req, config)); + self.as_mut().stream_future = + Some(Self::proxy_stream(stream, req, config)); } } } diff --git a/clash_lib/src/proxy/trojan/datagram.rs b/clash_lib/src/proxy/trojan/datagram.rs index aa5339c49..e51528697 100644 --- a/clash_lib/src/proxy/trojan/datagram.rs +++ b/clash_lib/src/proxy/trojan/datagram.rs @@ -61,7 +61,10 @@ impl Sink for OutboundDatagramTrojan { Poll::Ready(Ok(())) } - fn start_send(self: std::pin::Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + fn start_send( + self: std::pin::Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { let pin = self.get_mut(); pin.pkt = Some(item); pin.flushed = false; @@ -180,7 +183,10 @@ impl Stream for OutboundDatagramTrojan { *state = ReadState::Addr(atyp); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: {}", + err + ); return Poll::Ready(None); } } @@ -195,7 +201,11 @@ impl Stream for OutboundDatagramTrojan { *state = ReadState::Port(Addr::V4(ip)); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: \ + {}", + err + ); return Poll::Ready(None); } } @@ -209,7 +219,11 @@ impl Stream for OutboundDatagramTrojan { *state = ReadState::Port(Addr::V6(ip)); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: \ + {}", + err + ); return Poll::Ready(None); } } @@ -226,7 +240,8 @@ impl Stream for OutboundDatagramTrojan { Ok(n) => n, Err(err) => { debug!( - "failed to read socks addr from Trojan stream: {}", + "failed to read socks addr from Trojan \ + stream: {}", err ); return Poll::Ready(None); @@ -240,7 +255,8 @@ impl Stream for OutboundDatagramTrojan { Ok(domain) => domain, Err(err) => { debug!( - "failed to read socks addr from Trojan stream: {}", + "failed to read socks addr from Trojan \ + stream: {}", err ); return Poll::Ready(None); @@ -249,7 +265,11 @@ impl Stream for OutboundDatagramTrojan { *state = ReadState::Port(Addr::Domain(domain)); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: \ + {}", + err + ); return Poll::Ready(None); } } @@ -268,11 +288,15 @@ impl Stream for OutboundDatagramTrojan { Addr::V4(ip) => SocksAddr::from((*ip, port)), Addr::V6(ip) => SocksAddr::from((*ip, port)), Addr::Domain(domain) => { - match SocksAddr::try_from((domain.to_owned(), port)) { + match SocksAddr::try_from(( + domain.to_owned(), + port, + )) { Ok(addr) => addr, Err(err) => { debug!( - "failed to read socks addr from Trojan stream: {}", + "failed to read socks addr from \ + Trojan stream: {}", err ); return Poll::Ready(None); @@ -283,19 +307,26 @@ impl Stream for OutboundDatagramTrojan { *state = ReadState::DataLen(addr); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: {}", + err + ); return Poll::Ready(None); } } } ReadState::DataLen(addr) => { - // TODO: this is error prone, make this a more accurate state machine + // TODO: this is error prone, make this a more accurate + // state machine let fut = pin.read_u16(); pin_mut!(fut); let data_len = match ready!(fut.poll(cx)) { Ok(data_len) => data_len, Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: {}", + err + ); return Poll::Ready(None); } }; @@ -310,7 +341,10 @@ impl Stream for OutboundDatagramTrojan { } } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: {}", + err + ); return Poll::Ready(None); } }; @@ -342,7 +376,10 @@ impl Stream for OutboundDatagramTrojan { })); } Err(err) => { - debug!("failed to read socks addr from Trojan stream: {}", err); + debug!( + "failed to read socks addr from Trojan stream: {}", + err + ); return Poll::Ready(None); } } diff --git a/clash_lib/src/proxy/trojan/mod.rs b/clash_lib/src/proxy/trojan/mod.rs index 468a4aae5..5714e7cb1 100644 --- a/clash_lib/src/proxy/trojan/mod.rs +++ b/clash_lib/src/proxy/trojan/mod.rs @@ -1,34 +1,32 @@ use std::{io, sync::Arc}; use async_trait::async_trait; -use bytes::BufMut; -use bytes::BytesMut; +use bytes::{BufMut, BytesMut}; use futures::TryFutureExt; -use sha2::Digest; -use sha2::Sha224; +use sha2::{Digest, Sha224}; use tokio::io::AsyncWriteExt; -use crate::app::dispatcher::BoxedChainedDatagram; -use crate::app::dispatcher::ChainedDatagram; -use crate::app::dispatcher::ChainedDatagramWrapper; -use crate::app::dispatcher::ChainedStream; -use crate::app::dispatcher::ChainedStreamWrapper; -use crate::common::utils; use crate::{ - app::{dispatcher::BoxedChainedStream, dns::ThreadSafeDNSResolver}, + app::{ + dispatcher::{ + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, + }, + dns::ThreadSafeDNSResolver, + }, + common::utils, session::Session, }; use self::datagram::OutboundDatagramTrojan; -use super::transport; -use super::transport::TLSOptions; -use super::utils::RemoteConnector; -use super::ConnectorType; use super::{ options::{GrpcOption, WsOption}, - utils::new_tcp_stream, - AnyOutboundHandler, AnyStream, CommonOption, OutboundHandler, OutboundType, + transport, + transport::TLSOptions, + utils::{new_tcp_stream, RemoteConnector}, + AnyOutboundHandler, AnyStream, CommonOption, ConnectorType, OutboundHandler, + OutboundType, }; mod datagram; @@ -325,7 +323,8 @@ mod tests { }; let handler = Handler::new(opts); // ignore the udp test - run_test_suites_and_cleanup(handler, get_ws_runner().await?, Suite::all()).await + run_test_suites_and_cleanup(handler, get_ws_runner().await?, Suite::all()) + .await } async fn get_grpc_runner() -> anyhow::Result { @@ -364,6 +363,7 @@ mod tests { })), }; let handler = Handler::new(opts); - run_test_suites_and_cleanup(handler, get_grpc_runner().await?, Suite::all()).await + run_test_suites_and_cleanup(handler, get_grpc_runner().await?, Suite::all()) + .await } } diff --git a/clash_lib/src/proxy/tuic/compat.rs b/clash_lib/src/proxy/tuic/compat.rs index 997796060..25d97c063 100644 --- a/clash_lib/src/proxy/tuic/compat.rs +++ b/clash_lib/src/proxy/tuic/compat.rs @@ -11,22 +11,38 @@ use super::TuicDatagramOutbound; impl Sink for TuicDatagramOutbound { type Error = std::io::Error; - fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { self.send_tx .poll_ready_unpin(cx) .map_err(|v| new_io_error(&format!("{v:?}"))) } - fn start_send(mut self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + + fn start_send( + mut self: Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { self.send_tx .start_send_unpin(item) .map_err(|v| new_io_error(&format!("{v:?}"))) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { self.send_tx .poll_flush_unpin(cx) .map_err(|v| new_io_error(&format!("{v:?}"))) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { self.send_tx .poll_close_unpin(cx) .map_err(|v| new_io_error(&format!("{v:?}"))) @@ -35,7 +51,11 @@ impl Sink for TuicDatagramOutbound { impl Stream for TuicDatagramOutbound { type Item = UdpPacket; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { self.recv_rx.poll_recv(cx) } } diff --git a/clash_lib/src/proxy/tuic/handle_stream.rs b/clash_lib/src/proxy/tuic/handle_stream.rs index 6da1469dd..2ad936a9f 100644 --- a/clash_lib/src/proxy/tuic/handle_stream.rs +++ b/clash_lib/src/proxy/tuic/handle_stream.rs @@ -1,5 +1,4 @@ -use std::sync::atomic::Ordering; -use std::sync::Arc; +use std::sync::{atomic::Ordering, Arc}; use bytes::Bytes; use quinn::{RecvStream, SendStream, VarInt}; @@ -27,7 +26,9 @@ impl TuicConnection { Ok((recv, reg)) } - pub async fn accept_bi_stream(&self) -> anyhow::Result<(SendStream, RecvStream, Register)> { + pub async fn accept_bi_stream( + &self, + ) -> anyhow::Result<(SendStream, RecvStream, Register)> { let max = self.max_concurrent_bi_streams.load(Ordering::Relaxed); if self.remote_bi_stream_cnt.count() as u32 == max { @@ -47,7 +48,11 @@ impl TuicConnection { Ok(self.conn.read_datagram().await?) } - pub async fn handle_uni_stream(self: Arc, recv: RecvStream, _reg: Register) { + pub async fn handle_uni_stream( + self: Arc, + recv: RecvStream, + _reg: Register, + ) { tracing::debug!("[relay] incoming unidirectional stream"); let res = match self.inner.accept_uni_stream(recv).await { diff --git a/clash_lib/src/proxy/tuic/handle_task.rs b/clash_lib/src/proxy/tuic/handle_task.rs index 3309faf85..72d323349 100644 --- a/clash_lib/src/proxy/tuic/handle_task.rs +++ b/clash_lib/src/proxy/tuic/handle_task.rs @@ -7,13 +7,15 @@ use anyhow::Result; use tuic::Address; use tuic_quinn::{Connect, Packet}; -use crate::proxy::datagram::UdpPacket; -use crate::session::SocksAddr as ClashSocksAddr; +use crate::{proxy::datagram::UdpPacket, session::SocksAddr as ClashSocksAddr}; use super::types::{TuicConnection, UdpRelayMode}; impl TuicConnection { - pub async fn tuic_auth(self: Arc, zero_rtt_accepted: Option) { + pub async fn tuic_auth( + self: Arc, + zero_rtt_accepted: Option, + ) { if let Some(zero_rtt_accepted) = zero_rtt_accepted { tracing::debug!("[auth] waiting for connection to be fully established"); zero_rtt_accepted.await; @@ -43,7 +45,9 @@ impl TuicConnection { match self.inner.connect(addr).await { Ok(conn) => Ok(conn), Err(err) => { - tracing::warn!("[tcp] failed initializing relay to {addr_display}: {err}"); + tracing::warn!( + "[tcp] failed initializing relay to {addr_display}: {err}" + ); Err(anyhow!(err)) } } @@ -59,12 +63,15 @@ impl TuicConnection { match self.udp_relay_mode { UdpRelayMode::Native => { - tracing::info!("[udp] [{assoc_id:#06x}] [to-native] to {addr_display}"); + tracing::info!( + "[udp] [{assoc_id:#06x}] [to-native] to {addr_display}" + ); match self.inner.packet_native(pkt, addr, assoc_id) { Ok(()) => Ok(()), Err(err) => { tracing::warn!( - "[udp] [{assoc_id:#06x}] [to-native] to {addr_display}: {err}" + "[udp] [{assoc_id:#06x}] [to-native] to \ + {addr_display}: {err}" ); Err(anyhow!(err)) } @@ -76,7 +83,8 @@ impl TuicConnection { Ok(()) => Ok(()), Err(err) => { tracing::warn!( - "[udp] [{assoc_id:#06x}] [to-quic] to {addr_display}: {err}" + "[udp] [{assoc_id:#06x}] [to-quic] to {addr_display}: \ + {err}" ); Err(anyhow!(err)) } @@ -98,31 +106,50 @@ impl TuicConnection { }; tracing::info!( - "[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] fragment {frag_id}/{frag_total}", + "[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] fragment \ + {frag_id}/{frag_total}", frag_id = pkt.frag_id() + 1, frag_total = pkt.frag_total(), ); match pkt.accept().await { Ok(Some((data, remote_addr, _))) => { - tracing::info!("[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] from {remote_addr}"); - let (session, local_addr) = match self.udp_sessions.read().await.get(&assoc_id) { - Some(v) => (v.incoming.clone(), v.local_addr.clone()), - None => { - tracing::error!("[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] unable to find udp session"); - return; - }, - }; + tracing::info!( + "[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] from \ + {remote_addr}" + ); + let (session, local_addr) = + match self.udp_sessions.read().await.get(&assoc_id) { + Some(v) => (v.incoming.clone(), v.local_addr.clone()), + None => { + tracing::error!( + "[udp] [{assoc_id:#06x}] [from-{mode}] \ + [{pkt_id:#06x}] unable to find udp session" + ); + return; + } + }; let remote_addr = match remote_addr { Address::None => unreachable!(), - Address::DomainAddress(domain, port) => ClashSocksAddr::Domain(domain, port), + Address::DomainAddress(domain, port) => { + ClashSocksAddr::Domain(domain, port) + } Address::SocketAddress(socket) => ClashSocksAddr::Ip(socket), }; - if let Err(err) = session.send(UdpPacket::new(data.into(), remote_addr, local_addr)).await { - tracing::error!("[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] failed sending packet: {err}") + if let Err(err) = session + .send(UdpPacket::new(data.into(), remote_addr, local_addr)) + .await + { + tracing::error!( + "[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] \ + failed sending packet: {err}" + ) }; - }, + } Ok(None) => {} - Err(err) => tracing::error!("[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] packet receiving error: {err}"), + Err(err) => tracing::error!( + "[udp] [{assoc_id:#06x}] [from-{mode}] [{pkt_id:#06x}] packet \ + receiving error: {err}" + ), } } @@ -144,7 +171,9 @@ impl TuicConnection { } match self.inner.heartbeat().await { - Ok(()) => tracing::debug!("[tuic heartbeat] - {}", self.conn.remote_address()), + Ok(()) => { + tracing::debug!("[tuic heartbeat] - {}", self.conn.remote_address()) + } Err(err) => tracing::error!("[tuic heartbeat] - {err}"), } Ok(()) @@ -156,6 +185,7 @@ impl TuicConnection { self.inner.collect_garbage(gc_lifetime); Ok(()) } + /// Tasks triggered by timer /// Won't return unless occurs error pub async fn cyclical_tasks( diff --git a/clash_lib/src/proxy/tuic/mod.rs b/clash_lib/src/proxy/tuic/mod.rs index e81324bcd..c6815b8e1 100644 --- a/clash_lib/src/proxy/tuic/mod.rs +++ b/clash_lib/src/proxy/tuic/mod.rs @@ -3,17 +3,18 @@ mod handle_stream; mod handle_task; pub(crate) mod types; -use crate::proxy::tuic::types::SocketAdderTrans; -use crate::proxy::utils::new_udp_socket; +use crate::proxy::{tuic::types::SocketAdderTrans, utils::new_udp_socket}; use anyhow::Result; use axum::async_trait; -use quinn::congestion::{BbrConfig, NewRenoConfig}; -use quinn::{EndpointConfig, TokioRuntime}; +use quinn::{ + congestion::{BbrConfig, NewRenoConfig}, + EndpointConfig, TokioRuntime, +}; use tracing::debug; -use std::net::{Ipv4Addr, Ipv6Addr}; use std::{ + net::{Ipv4Addr, Ipv6Addr}, sync::{ atomic::{AtomicU16, Ordering}, Arc, @@ -27,8 +28,8 @@ use uuid::Uuid; use crate::{ app::{ dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, ChainedDatagramWrapper, - ChainedStream, ChainedStreamWrapper, + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, }, @@ -38,20 +39,21 @@ use crate::{ }; use crate::session::SocksAddr as ClashSocksAddr; -use quinn::ClientConfig as QuinnConfig; -use quinn::Endpoint as QuinnEndpoint; -use quinn::TransportConfig as QuinnTransportConfig; -use quinn::{congestion::CubicConfig, VarInt}; +use quinn::{ + congestion::CubicConfig, ClientConfig as QuinnConfig, Endpoint as QuinnEndpoint, + TransportConfig as QuinnTransportConfig, VarInt, +}; use tokio::sync::{Mutex as AsyncMutex, OnceCell}; use rustls::client::ClientConfig as TlsConfig; use self::types::{CongestionControl, TuicConnection, UdpRelayMode, UdpSession}; -use super::utils::{get_outbound_interface, Interface}; -use super::ConnectorType; use super::{ - datagram::UdpPacket, AnyOutboundDatagram, AnyOutboundHandler, OutboundHandler, OutboundType, + datagram::UdpPacket, + utils::{get_outbound_interface, Interface}, + AnyOutboundDatagram, AnyOutboundHandler, ConnectorType, OutboundHandler, + OutboundType, }; #[derive(Debug, Clone)] @@ -117,6 +119,7 @@ impl OutboundHandler for Handler { std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) }) } + async fn connect_datagram( &self, sess: &Session, @@ -155,7 +158,9 @@ impl Handler { .unwrap() .with_root_certificates(GLOBAL_ROOT_STORE.clone()) .with_no_client_auth(); - // TODO(error-handling) if alpn not match the following error will be throw: aborted by peer: the cryptographic handshake failed: error 120: peer doesn't support any known protocol + // TODO(error-handling) if alpn not match the following error will be + // throw: aborted by peer: the cryptographic handshake failed: error + // 120: peer doesn't support any known protocol crypto.alpn_protocols.clone_from(&opts.alpn); crypto.enable_early_data = true; crypto.enable_sni = !opts.disable_sni; @@ -168,15 +173,12 @@ impl Handler { .stream_receive_window(opts.receive_window) .max_idle_timeout(Some(opts.idle_timeout.try_into().unwrap())); match opts.congestion_controller { - CongestionControl::Cubic => { - transport_config.congestion_controller_factory(Arc::new(CubicConfig::default())) - } - CongestionControl::NewReno => { - transport_config.congestion_controller_factory(Arc::new(NewRenoConfig::default())) - } - CongestionControl::Bbr => { - transport_config.congestion_controller_factory(Arc::new(BbrConfig::default())) - } + CongestionControl::Cubic => transport_config + .congestion_controller_factory(Arc::new(CubicConfig::default())), + CongestionControl::NewReno => transport_config + .congestion_controller_factory(Arc::new(NewRenoConfig::default())), + CongestionControl::Bbr => transport_config + .congestion_controller_factory(Arc::new(BbrConfig::default())), }; quinn_config.transport_config(Arc::new(transport_config)); @@ -217,7 +219,9 @@ impl Handler { ep: endpoint, server: ServerAddr::new(opts.server.clone(), opts.port, None), uuid: opts.uuid, - password: Arc::from(opts.password.clone().into_bytes().into_boxed_slice()), + password: Arc::from( + opts.password.clone().into_bytes().into_boxed_slice(), + ), udp_relay_mode: opts.udp_relay_mode, zero_rtt_handshake: opts.reduce_rtt, heartbeat: opts.heartbeat_interval, @@ -228,10 +232,15 @@ impl Handler { Ok(endpoint) } - async fn get_conn(&self, resolver: &ThreadSafeDNSResolver) -> Result> { + async fn get_conn( + &self, + resolver: &ThreadSafeDNSResolver, + ) -> Result> { let endpoint = self .ep - .get_or_try_init(|| Self::init_endpoint(self.opts.clone(), resolver.clone())) + .get_or_try_init(|| { + Self::init_endpoint(self.opts.clone(), resolver.clone()) + }) .await?; let fut = async { @@ -321,7 +330,9 @@ impl TuicDatagramOutbound { } } // TuicDatagramOutbound dropped or outgoing_udp occurs error - tracing::info!("[udp] [dissociate] closing UDP session [{assoc_id:#06x}]"); + tracing::info!( + "[udp] [dissociate] closing UDP session [{assoc_id:#06x}]" + ); _ = conn.dissociate(assoc_id).await; udp_sessions.write().await.remove(&assoc_id); anyhow::Ok(()) diff --git a/clash_lib/src/proxy/tuic/types.rs b/clash_lib/src/proxy/tuic/types.rs index e799b2848..3306be2b0 100644 --- a/clash_lib/src/proxy/tuic/types.rs +++ b/clash_lib/src/proxy/tuic/types.rs @@ -1,13 +1,16 @@ -use crate::app::dns::ThreadSafeDNSResolver; -use crate::proxy::utils::{get_outbound_interface, new_udp_socket, Interface}; -use crate::session::SocksAddr as ClashSocksAddr; +use crate::{ + app::dns::ThreadSafeDNSResolver, + proxy::utils::{get_outbound_interface, new_udp_socket, Interface}, + session::SocksAddr as ClashSocksAddr, +}; use anyhow::Result; -use quinn::Connection as QuinnConnection; -use quinn::{Endpoint as QuinnEndpoint, ZeroRttAccepted}; +use quinn::{ + Connection as QuinnConnection, Endpoint as QuinnEndpoint, ZeroRttAccepted, +}; use register_count::Counter; -use std::collections::HashMap; use std::{ + collections::HashMap, net::{IpAddr, SocketAddr}, sync::{atomic::AtomicU32, Arc}, time::Duration, @@ -38,7 +41,8 @@ impl TuicEndpoint { ) -> Result> { let remote_addr = self.server.resolve(resolver).await?; let connect_to = async { - // if client and server don't match each other or forced to rebind, then rebind local socket + // if client and server don't match each other or forced to rebind, + // then rebind local socket if rebind { debug!("rebinding endpoint UDP socket"); @@ -55,9 +59,9 @@ impl TuicEndpoint { debug!("rebound endpoint UDP socket to {}", socket.local_addr()?); - self.ep - .rebind(socket.into_std()?) - .map_err(|err| anyhow!("failed to rebind endpoint UDP socket {}", err))?; + self.ep.rebind(socket.into_std()?).map_err(|err| { + anyhow!("failed to rebind endpoint UDP socket {}", err) + })?; } tracing::trace!( @@ -122,6 +126,7 @@ impl TuicConnection { None => Ok(()), } } + #[allow(clippy::too_many_arguments)] fn new( conn: QuinnConnection, @@ -141,19 +146,23 @@ impl TuicConnection { udp_relay_mode, remote_uni_stream_cnt: Counter::new(), remote_bi_stream_cnt: Counter::new(), - // TODO: seems tuic dynamicly adjust the size of max concurrent streams, is it necessary to configure the stream size? + // TODO: seems tuic dynamicly adjust the size of max concurrent + // streams, is it necessary to configure the stream size? max_concurrent_uni_streams: Arc::new(AtomicU32::new(32)), max_concurrent_bi_streams: Arc::new(AtomicU32::new(32)), udp_sessions: Arc::new(AsyncRwLock::new(HashMap::new())), }; let conn = Arc::new(conn); - tokio::spawn( - conn.clone() - .init(zero_rtt_accepted, heartbeat, gc_interval, gc_lifetime), - ); + tokio::spawn(conn.clone().init( + zero_rtt_accepted, + heartbeat, + gc_interval, + gc_lifetime, + )); conn } + async fn init( self: Arc, zero_rtt_accepted: Option, @@ -165,10 +174,11 @@ impl TuicConnection { // TODO check the cancellation safety of tuic_auth tokio::spawn(self.clone().tuic_auth(zero_rtt_accepted)); - tokio::spawn( - self.clone() - .cyclical_tasks(heartbeat, gc_interval, gc_lifetime), - ); + tokio::spawn(self.clone().cyclical_tasks( + heartbeat, + gc_interval, + gc_lifetime, + )); let err = loop { tokio::select! { @@ -205,7 +215,10 @@ impl ServerAddr { &self.domain } - pub async fn resolve(&self, resolver: &ThreadSafeDNSResolver) -> Result { + pub async fn resolve( + &self, + resolver: &ThreadSafeDNSResolver, + ) -> Result { if let Some(ip) = self.ip { Ok(SocketAddr::from((ip, self.port))) } else { @@ -246,12 +259,16 @@ impl From<&str> for CongestionControl { fn from(s: &str) -> Self { if s.eq_ignore_ascii_case("cubic") { Self::Cubic - } else if s.eq_ignore_ascii_case("new_reno") || s.eq_ignore_ascii_case("newreno") { + } else if s.eq_ignore_ascii_case("new_reno") + || s.eq_ignore_ascii_case("newreno") + { Self::NewReno } else if s.eq_ignore_ascii_case("bbr") { Self::Bbr } else { - tracing::warn!("Unknown congestion controller {s}. Use default controller"); + tracing::warn!( + "Unknown congestion controller {s}. Use default controller" + ); Self::default() } } @@ -271,7 +288,9 @@ impl SocketAdderTrans for crate::session::SocksAddr { use crate::session::SocksAddr; match self { SocksAddr::Ip(addr) => tuic::Address::SocketAddress(addr), - SocksAddr::Domain(domain, port) => tuic::Address::DomainAddress(domain, port), + SocksAddr::Domain(domain, port) => { + tuic::Address::DomainAddress(domain, port) + } } } } diff --git a/clash_lib/src/proxy/tun/datagram.rs b/clash_lib/src/proxy/tun/datagram.rs index d8f2ec966..07b223628 100644 --- a/clash_lib/src/proxy/tun/datagram.rs +++ b/clash_lib/src/proxy/tun/datagram.rs @@ -67,7 +67,10 @@ impl Sink for TunDatagram { Poll::Ready(Ok(())) } - fn start_send(self: std::pin::Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + fn start_send( + self: std::pin::Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { let pin = self.get_mut(); pin.pkt = Some(item); pin.flushed = false; diff --git a/clash_lib/src/proxy/tun/inbound.rs b/clash_lib/src/proxy/tun/inbound.rs index 92edd8a38..a3583284d 100644 --- a/clash_lib/src/proxy/tun/inbound.rs +++ b/clash_lib/src/proxy/tun/inbound.rs @@ -58,8 +58,9 @@ async fn handle_inbound_datagram( // forward packets from tun to dispatcher let (d_tx, d_rx) = tokio::sync::mpsc::channel::(32); - // 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 + // 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, local_addr); let sess = Session { @@ -146,8 +147,8 @@ pub fn get_runner( let device_id = cfg.device_id; - let u = - Url::parse(&device_id).map_err(|x| Error::InvalidConfig(format!("tun device {}", x)))?; + let u = Url::parse(&device_id) + .map_err(|x| Error::InvalidConfig(format!("tun device {}", x)))?; let mut tun_cfg = tun::Configuration::default(); @@ -217,7 +218,9 @@ pub fn get_runner( while let Some(pkt) = tun_stream.next().await { match pkt { Ok(pkt) => { - if let Err(e) = stack_sink.send(pkt.into_bytes().into()).await { + if let Err(e) = + stack_sink.send(pkt.into_bytes().into()).await + { error!("failed to send pkt to stack: {}", e); break; } @@ -234,7 +237,9 @@ pub fn get_runner( let dsp = dispatcher.clone(); futs.push(Box::pin(async move { - while let Some((stream, local_addr, remote_addr)) = tcp_listener.next().await { + while let Some((stream, local_addr, remote_addr)) = + tcp_listener.next().await + { tokio::spawn(handle_inbound_stream( stream, local_addr, diff --git a/clash_lib/src/proxy/urltest/mod.rs b/clash_lib/src/proxy/urltest/mod.rs index d39afbbdd..ecb9df5da 100644 --- a/clash_lib/src/proxy/urltest/mod.rs +++ b/clash_lib/src/proxy/urltest/mod.rs @@ -209,7 +209,8 @@ impl OutboundHandler for Handler { ); m.insert( "all".to_string(), - Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) as _, + Box::new(all.iter().map(|x| x.name().to_owned()).collect::>()) + as _, ); m } diff --git a/clash_lib/src/proxy/utils/provider_helper.rs b/clash_lib/src/proxy/utils/provider_helper.rs index fbf9fd049..4d2d7cc45 100644 --- a/clash_lib/src/proxy/utils/provider_helper.rs +++ b/clash_lib/src/proxy/utils/provider_helper.rs @@ -13,7 +13,8 @@ pub async fn get_proxies_from_providers( provider.read().await.touch().await; } - let mut proxies_from_provider = provider.read().await.proxies().await.to_vec(); + let mut proxies_from_provider = + provider.read().await.proxies().await.to_vec(); proxies.append(&mut proxies_from_provider); } proxies diff --git a/clash_lib/src/proxy/utils/proxy_connector.rs b/clash_lib/src/proxy/utils/proxy_connector.rs index ba227740f..892a2d043 100644 --- a/clash_lib/src/proxy/utils/proxy_connector.rs +++ b/clash_lib/src/proxy/utils/proxy_connector.rs @@ -6,11 +6,15 @@ use tracing::trace; use crate::{ app::{ dispatcher::{ - ChainedDatagram, ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, + ChainedDatagram, ChainedDatagramWrapper, ChainedStream, + ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, }, - proxy::{datagram::OutboundDatagramImpl, AnyOutboundDatagram, AnyOutboundHandler, AnyStream}, + proxy::{ + datagram::OutboundDatagramImpl, AnyOutboundDatagram, AnyOutboundHandler, + AnyStream, + }, session::{Network, Session, SocksAddr, Type}, }; @@ -25,7 +29,9 @@ pub trait RemoteConnector: Send + Sync { address: &str, port: u16, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result; async fn connect_datagram( @@ -34,7 +40,9 @@ pub trait RemoteConnector: Send + Sync { src: Option<&SocketAddr>, destination: &SocksAddr, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result; } @@ -54,7 +62,9 @@ impl RemoteConnector for DirectConnector { address: &str, port: u16, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result { new_tcp_stream( resolver, @@ -73,7 +83,9 @@ impl RemoteConnector for DirectConnector { src: Option<&SocketAddr>, _destination: &SocksAddr, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result { let dgram = new_udp_socket( src, @@ -95,7 +107,10 @@ pub struct ProxyConnector { } impl ProxyConnector { - pub fn new(proxy: AnyOutboundHandler, connector: Box) -> Self { + pub fn new( + proxy: AnyOutboundHandler, + connector: Box, + ) -> Self { Self { proxy, connector } } } @@ -108,7 +123,9 @@ impl RemoteConnector for ProxyConnector { address: &str, port: u16, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result { let sess = Session { network: Network::Tcp, @@ -143,7 +160,9 @@ impl RemoteConnector for ProxyConnector { _src: Option<&SocketAddr>, destination: &SocksAddr, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< + u32, + >, ) -> std::io::Result { let sess = Session { network: Network::Udp, @@ -156,7 +175,11 @@ impl RemoteConnector for ProxyConnector { }; let s = self .proxy - .connect_datagram_with_connector(&sess, resolver, self.connector.as_ref()) + .connect_datagram_with_connector( + &sess, + resolver, + self.connector.as_ref(), + ) .await?; let stream = ChainedDatagramWrapper::new(s); diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index 1781815f4..2d33caf01 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -41,9 +41,13 @@ pub fn apply_tcp_options(s: TcpStream) -> std::io::Result { } } -fn must_bind_socket_on_interface(socket: &socket2::Socket, iface: &Interface) -> io::Result<()> { +fn must_bind_socket_on_interface( + socket: &socket2::Socket, + iface: &Interface, +) -> io::Result<()> { match iface { - // TODO: should this be ever used vs. calling .bind(2) from the caller side? + // TODO: should this be ever used vs. calling .bind(2) from the caller + // side? Interface::IpAddr(ip) => socket.bind(&SocketAddr::new(*ip, 0).into()), Interface::Name(name) => { #[cfg(target_vendor = "apple")] @@ -52,7 +56,11 @@ fn must_bind_socket_on_interface(socket: &socket2::Socket, iface: &Interface) -> libc::if_nametoindex(name.as_str().as_ptr() as *const _) })) } - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux" + ))] { socket.bind_device(Some(name.as_bytes())) } @@ -78,7 +86,9 @@ pub async fn new_tcp_stream<'a>( let dial_addr = resolver .resolve(address, false) .await - .map_err(|v| io::Error::new(io::ErrorKind::Other, format!("dns failure: {}", v)))? + .map_err(|v| { + io::Error::new(io::ErrorKind::Other, format!("dns failure: {}", v)) + })? .ok_or(io::Error::new( io::ErrorKind::Other, format!("can't resolve dns: {}", address), @@ -135,12 +145,22 @@ pub async fn new_udp_socket( let socket = match src { Some(src) => { if src.is_ipv4() { - socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)? + socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::DGRAM, + None, + )? } else { - socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None)? + socket2::Socket::new( + socket2::Domain::IPV6, + socket2::Type::DGRAM, + None, + )? } } - None => socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)?, + None => { + socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)? + } }; match (src, iface) { @@ -190,9 +210,12 @@ mod tests { for i in 0..100 { futs.push(tokio::spawn(async move { let now = std::time::Instant::now(); - let socket = - socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None) - .unwrap(); + let socket = socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::DGRAM, + None, + ) + .unwrap(); timeout( Duration::from_secs(10), diff --git a/clash_lib/src/proxy/utils/test_utils/config_helper.rs b/clash_lib/src/proxy/utils/test_utils/config_helper.rs index 05b713114..3a0d4eae5 100644 --- a/clash_lib/src/proxy/utils/test_utils/config_helper.rs +++ b/clash_lib/src/proxy/utils/test_utils/config_helper.rs @@ -1,6 +1,5 @@ use crate::Error; -use std::path::PathBuf; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tracing::debug; use crate::{ @@ -36,10 +35,12 @@ pub async fn load_config() -> anyhow::Result<( let mmdb_path = test_base_dir.join("Country.mmdb"); let system_resolver = Arc::new(SystemResolver::new().map_err(|x| Error::DNSError(x.to_string()))?); - let client = new_http_client(system_resolver).map_err(|x| Error::DNSError(x.to_string()))?; + let client = new_http_client(system_resolver) + .map_err(|x| Error::DNSError(x.to_string()))?; let mmdb = Arc::new( - mmdb::Mmdb::new(mmdb_path, config.general.mmdb_download_url.clone(), client).await?, + mmdb::Mmdb::new(mmdb_path, config.general.mmdb_download_url.clone(), client) + .await?, ); debug!("initializing cache store"); @@ -49,7 +50,8 @@ pub async fn load_config() -> anyhow::Result<( ); let dns_resolver: Arc = - dns::Resolver::new_resolver(&config.dns, cache_store.clone(), mmdb.clone()).await; + dns::Resolver::new_resolver(&config.dns, cache_store.clone(), mmdb.clone()) + .await; Ok((config, dns_resolver)) } diff --git a/clash_lib/src/proxy/utils/test_utils/docker_runner.rs b/clash_lib/src/proxy/utils/test_utils/docker_runner.rs index 5d177e458..3d09318ba 100644 --- a/clash_lib/src/proxy/utils/test_utils/docker_runner.rs +++ b/clash_lib/src/proxy/utils/test_utils/docker_runner.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; -use bollard::container::{Config, LogsOptions, RemoveContainerOptions}; -use bollard::secret::{HostConfig, Mount, PortBinding}; -use bollard::Docker; +use bollard::{ + container::{Config, LogsOptions, RemoveContainerOptions}, + secret::{HostConfig, Mount, PortBinding}, + Docker, +}; use bollard::image::CreateImageOptions; @@ -83,14 +85,18 @@ impl MultiDockerTestRunner { Self { runners } } - pub async fn add(&mut self, creator: impl Future>) { + pub async fn add( + &mut self, + creator: impl Future>, + ) { match creator.await { Ok(runner) => { self.runners.push(runner); } Err(e) => { tracing::warn!( - "cannot start container, please check the docker environment, error: {:?}", + "cannot start container, please check the docker environment, \ + error: {:?}", e ); } @@ -257,7 +263,8 @@ impl DockerTestRunnerBuilder { } pub fn cap_add(mut self, caps: &[&str]) -> Self { - self.host_config.cap_add = Some(caps.iter().map(|x| x.to_string()).collect()); + self.host_config.cap_add = + Some(caps.iter().map(|x| x.to_string()).collect()); self } diff --git a/clash_lib/src/proxy/utils/test_utils/mod.rs b/clash_lib/src/proxy/utils/test_utils/mod.rs index 749d009ff..a8f933118 100644 --- a/clash_lib/src/proxy/utils/test_utils/mod.rs +++ b/clash_lib/src/proxy/utils/test_utils/mod.rs @@ -25,8 +25,12 @@ pub mod consts; pub mod docker_runner; // TODO: add the throughput metrics -pub async fn ping_pong_test(handler: Arc, port: u16) -> anyhow::Result<()> { - // PATH: our proxy handler -> proxy-server(container) -> target local server(127.0.0.1:port) +pub async fn ping_pong_test( + handler: Arc, + port: u16, +) -> anyhow::Result<()> { + // PATH: our proxy handler -> proxy-server(container) -> target local + // server(127.0.0.1:port) let sess = Session { destination: ("127.0.0.1".to_owned(), port) @@ -72,7 +76,10 @@ pub async fn ping_pong_test(handler: Arc, port: u16) -> any loop { let (stream, _) = listener.accept().await?; - tracing::info!("Accepted connection from: {}", stream.peer_addr().unwrap()); + tracing::info!( + "Accepted connection from: {}", + stream.peer_addr().unwrap() + ); destination_fn(stream).await? } }); @@ -131,7 +138,8 @@ pub async fn ping_pong_udp_test( handler: Arc, port: u16, ) -> anyhow::Result<()> { - // PATH: our proxy handler -> proxy-server(container) -> target local server(127.0.0.1:port) + // PATH: our proxy handler -> proxy-server(container) -> target local + // server(127.0.0.1:port) let src = ("127.0.0.1".to_owned(), 10005) .try_into() @@ -168,8 +176,9 @@ pub async fn ping_pong_udp_test( Ok(()) } - let target_local_server_handler: tokio::task::JoinHandle> = - tokio::spawn(async move { destination_fn(listener).await }); + let target_local_server_handler: tokio::task::JoinHandle< + Result<(), anyhow::Error>, + > = tokio::spawn(async move { destination_fn(listener).await }); async fn proxy_fn( mut datagram: BoxedChainedDatagram, @@ -216,7 +225,9 @@ pub async fn ping_pong_udp_test( } // latency test of the proxy, will reuse the `url_test` ability -pub async fn latency_test(handler: Arc) -> anyhow::Result<(u16, u16)> { +pub async fn latency_test( + handler: Arc, +) -> anyhow::Result<(u16, u16)> { let (_, resolver) = config_helper::load_config().await?; let proxy_manager = ProxyManager::new(resolver.clone()); proxy_manager diff --git a/clash_lib/src/proxy/vmess/mod.rs b/clash_lib/src/proxy/vmess/mod.rs index c0769e119..9b077f614 100644 --- a/clash_lib/src/proxy/vmess/mod.rs +++ b/clash_lib/src/proxy/vmess/mod.rs @@ -9,8 +9,8 @@ mod vmess_impl; use crate::{ app::{ dispatcher::{ - BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, ChainedDatagramWrapper, - ChainedStream, ChainedStreamWrapper, + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, }, dns::ThreadSafeDNSResolver, }, @@ -24,7 +24,8 @@ use super::{ options::{GrpcOption, Http2Option, HttpOption, WsOption}, transport::{self, Http2Config}, utils::{new_tcp_stream, RemoteConnector}, - AnyOutboundHandler, AnyStream, CommonOption, ConnectorType, OutboundHandler, OutboundType, + AnyOutboundHandler, AnyStream, CommonOption, ConnectorType, OutboundHandler, + OutboundType, }; pub enum VmessTransport { @@ -79,7 +80,12 @@ impl Handler { ); if let Some(tls_opt) = &self.opts.tls { - stream = transport::tls::wrap_stream(stream, tls_opt.to_owned(), None).await?; + stream = transport::tls::wrap_stream( + stream, + tls_opt.to_owned(), + None, + ) + .await?; } ws_builder.proxy_stream(stream).await? @@ -89,7 +95,8 @@ impl Handler { Some(tls_opt) => { let mut tls_opt = tls_opt.clone(); tls_opt.alpn = Some(vec!["h2".to_string()]); - transport::tls::wrap_stream(stream, tls_opt.to_owned(), None).await? + transport::tls::wrap_stream(stream, tls_opt.to_owned(), None) + .await? } None => stream, }; @@ -106,7 +113,8 @@ impl Handler { Some(VmessTransport::Grpc(ref opt)) => { stream = match self.opts.tls.as_ref() { Some(tls_opt) => { - transport::tls::wrap_stream(stream, tls_opt.to_owned(), None).await? + transport::tls::wrap_stream(stream, tls_opt.to_owned(), None) + .await? } None => stream, }; @@ -125,7 +133,12 @@ impl Handler { } None => { if let Some(tls_opt) = self.opts.tls.as_ref() { - stream = transport::tls::wrap_stream(stream, tls_opt.to_owned(), None).await?; + stream = transport::tls::wrap_stream( + stream, + tls_opt.to_owned(), + None, + ) + .await?; } stream } @@ -398,7 +411,8 @@ mod tests { })), }; let handler = Handler::new(opts); - run_test_suites_and_cleanup(handler, get_grpc_runner().await?, Suite::all()).await + run_test_suites_and_cleanup(handler, get_grpc_runner().await?, Suite::all()) + .await } async fn get_h2_runner() -> anyhow::Result { @@ -441,6 +455,7 @@ mod tests { })), }; let handler = Handler::new(opts); - run_test_suites_and_cleanup(handler, get_h2_runner().await?, Suite::all()).await + run_test_suites_and_cleanup(handler, get_h2_runner().await?, Suite::all()) + .await } } diff --git a/clash_lib/src/proxy/vmess/vmess_impl/cipher.rs b/clash_lib/src/proxy/vmess/vmess_impl/cipher.rs index 8bb2e8c75..20f7c28d3 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/cipher.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/cipher.rs @@ -15,6 +15,7 @@ impl VmessSecurity { pub fn overhead_len(&self) -> usize { 16 } + #[inline(always)] pub fn nonce_len(&self) -> usize { 12 @@ -51,7 +52,8 @@ impl AeadCipher { let nonce = &nonce[..security.nonce_len()]; match security { VmessSecurity::Aes128Gcm(cipher) => { - let dec = cipher.decrypt_in_place_with_slice(nonce, &[], &mut buf[..]); + let dec = + cipher.decrypt_in_place_with_slice(nonce, &[], &mut buf[..]); if let Err(err) = dec { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, @@ -60,7 +62,8 @@ impl AeadCipher { } } VmessSecurity::ChaCha20Poly1305(cipher) => { - let dec = cipher.decrypt_in_place_with_slice(nonce, &[], &mut buf[..]); + let dec = + cipher.decrypt_in_place_with_slice(nonce, &[], &mut buf[..]); if let Err(err) = dec { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, diff --git a/clash_lib/src/proxy/vmess/vmess_impl/client.rs b/clash_lib/src/proxy/vmess/vmess_impl/client.rs index 2027cc7b6..555e5fb6e 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/client.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/client.rs @@ -30,7 +30,8 @@ impl Builder { let uuid = uuid::Uuid::parse_str(&opt.uuid).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, - "invalid uuid format, should be xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "invalid uuid format, should be \ + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", ) })?; diff --git a/clash_lib/src/proxy/vmess/vmess_impl/datagram.rs b/clash_lib/src/proxy/vmess/vmess_impl/datagram.rs index fd167b314..d2c2671a6 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/datagram.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/datagram.rs @@ -50,7 +50,10 @@ impl Sink for OutboundDatagramVmess { Poll::Ready(Ok(())) } - fn start_send(self: std::pin::Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + fn start_send( + self: std::pin::Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { let pin = self.get_mut(); pin.pkt = Some(item); pin.flushed = false; @@ -81,7 +84,8 @@ impl Sink for OutboundDatagramVmess { if let Some(pkt) = pkt_container { if &pkt.dst_addr != remote_addr { error!( - "udp packet dst_addr not match, pkt.dst_addr: {}, remote_addr: {}", + "udp packet dst_addr not match, pkt.dst_addr: {}, remote_addr: \ + {}", pkt.dst_addr, remote_addr ); return Poll::Ready(Err(io::Error::new( @@ -93,7 +97,8 @@ impl Sink for OutboundDatagramVmess { if written.is_none() { let n = ready!(inner.as_mut().poll_write(cx, pkt.data.as_ref()))?; debug!( - "send udp packet to remote vmess server, len: {}, remote_addr: {}, dst_addr: {}", + "send udp packet to remote vmess server, len: {}, remote_addr: \ + {}, dst_addr: {}", n, remote_addr, pkt.dst_addr ); *written = Some(n); diff --git a/clash_lib/src/proxy/vmess/vmess_impl/header.rs b/clash_lib/src/proxy/vmess/vmess_impl/header.rs index 7ccee19b5..0f2cab2ce 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/header.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/header.rs @@ -5,7 +5,8 @@ use bytes::{Buf, BufMut, BytesMut}; use crate::common::{crypto, errors::map_io_error, utils}; use super::kdf::{ - self, KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY, KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_IV, + self, KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY, + KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_IV, KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_KEY, KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_LENGTH_AEAD_IV, KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_LENGTH_AEAD_KEY, @@ -22,7 +23,10 @@ fn create_auth_id(cmd_key: [u8; 16], timestamp: u64) -> [u8; 16] { let zero = crc32fast::hash(buf.as_ref()); buf.put_u32(zero); - let pk = kdf::vmess_kdf_1_one_shot(&cmd_key[..], KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY); + let pk = kdf::vmess_kdf_1_one_shot( + &cmd_key[..], + KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY, + ); let pk: [u8; 16] = pk[..16].try_into().unwrap(); // That's wired let key = GenericArray::from(pk); let cipher = aes::Aes128::new(&key); @@ -124,7 +128,10 @@ mod tests { let cmd_key = "1234567890123456".as_bytes(); - let pk = kdf::vmess_kdf_1_one_shot(cmd_key, KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY); + let pk = kdf::vmess_kdf_1_one_shot( + cmd_key, + KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY, + ); let pk: [u8; 16] = pk[..16].try_into().unwrap(); // That's wired let key = GenericArray::from(pk); let cipher = aes::Aes128::new(&key); @@ -135,7 +142,10 @@ mod tests { let block: [u8; 16] = block.as_slice()[..16].try_into().unwrap(); assert_eq!( block.to_vec(), - vec![55, 189, 144, 149, 192, 213, 241, 57, 37, 21, 179, 197, 135, 54, 86, 79] + vec![ + 55, 189, 144, 149, 192, 213, 241, 57, 37, 21, 179, 197, 135, 54, 86, + 79 + ] ); } @@ -197,10 +207,11 @@ mod tests { assert_eq!( out.freeze().to_vec(), [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 101, 25, 33, 23, 247, 66, 8, - 94, 171, 181, 162, 176, 21, 19, 111, 34, 161, 0, 0, 0, 0, 0, 0, 0, 0, 199, 22, 13, - 123, 209, 206, 78, 166, 69, 198, 7, 29, 224, 54, 214, 146, 73, 34, 66, 61, 10, 203, - 144, 160, 81, 17, 17, 191, 39, 197, 163, 246 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 101, 25, 33, + 23, 247, 66, 8, 94, 171, 181, 162, 176, 21, 19, 111, 34, 161, 0, 0, + 0, 0, 0, 0, 0, 0, 199, 22, 13, 123, 209, 206, 78, 166, 69, 198, 7, + 29, 224, 54, 214, 146, 73, 34, 66, 61, 10, 203, 144, 160, 81, 17, + 17, 191, 39, 197, 163, 246 ] ); } diff --git a/clash_lib/src/proxy/vmess/vmess_impl/kdf.rs b/clash_lib/src/proxy/vmess/vmess_impl/kdf.rs index e378bc774..1163e0587 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/kdf.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/kdf.rs @@ -1,21 +1,27 @@ // https://github.com/Qv2ray/v2ray-rust/blob/1df95e479bd2844a484c663d7727fae82b3efabe/src/proxy/vmess/kdf.rs -use hmac::Hmac; -use hmac::Mac; +use hmac::{Hmac, Mac}; use sha2::Sha256; type HmacSha256 = Hmac; const IPAD: u8 = 0x36; const OPAD: u8 = 0x5C; -pub const KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY: &[u8; 22] = b"AES Auth ID Encryption"; -pub const KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY: &[u8; 24] = b"AEAD Resp Header Len Key"; -pub const KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV: &[u8; 23] = b"AEAD Resp Header Len IV"; -pub const KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY: &[u8; 20] = b"AEAD Resp Header Key"; -pub const KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV: &[u8; 19] = b"AEAD Resp Header IV"; +pub const KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY: &[u8; 22] = + b"AES Auth ID Encryption"; +pub const KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY: &[u8; 24] = + b"AEAD Resp Header Len Key"; +pub const KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV: &[u8; 23] = + b"AEAD Resp Header Len IV"; +pub const KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY: &[u8; 20] = + b"AEAD Resp Header Key"; +pub const KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV: &[u8; 19] = + b"AEAD Resp Header IV"; pub const KDF_SALT_CONST_VMESS_AEAD_KDF: &[u8; 14] = b"VMess AEAD KDF"; -pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_KEY: &[u8; 21] = b"VMess Header AEAD Key"; -pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_IV: &[u8; 23] = b"VMess Header AEAD Nonce"; +pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_KEY: &[u8; 21] = + b"VMess Header AEAD Key"; +pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_AEAD_IV: &[u8; 23] = + b"VMess Header AEAD Nonce"; pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_LENGTH_AEAD_KEY: &[u8; 28] = b"VMess Header AEAD Key_Length"; pub const KDF_SALT_CONST_VMESS_HEADER_PAYLOAD_LENGTH_AEAD_IV: &[u8; 30] = @@ -159,7 +165,12 @@ fn get_vmess_kdf_3(key1: &[u8], key2: &[u8], key3: &[u8]) -> VmessKdf3 { VmessKdf3::new(get_vmess_kdf_2(key1, key2), key3) } -pub fn vmess_kdf_3_one_shot(id: &[u8], key1: &[u8], key2: &[u8], key3: &[u8]) -> [u8; 32] { +pub fn vmess_kdf_3_one_shot( + id: &[u8], + key1: &[u8], + key2: &[u8], + key3: &[u8], +) -> [u8; 32] { let mut h = get_vmess_kdf_3(key1, key2, key3); h.update(id); h.finalize() @@ -168,18 +179,24 @@ pub fn vmess_kdf_3_one_shot(id: &[u8], key1: &[u8], key2: &[u8], key3: &[u8]) -> #[cfg(test)] mod tests { use crate::proxy::vmess::vmess_impl::kdf::{ - vmess_kdf_1_one_shot, vmess_kdf_3_one_shot, KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, - KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, + vmess_kdf_1_one_shot, vmess_kdf_3_one_shot, + KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, + KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, + KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY, }; #[test] fn test_kdf_1_one_shot() { assert_eq!( - vmess_kdf_1_one_shot("test".as_bytes(), KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY), + vmess_kdf_1_one_shot( + "test".as_bytes(), + KDF_SALT_CONST_AUTH_ID_ENCRYPTION_KEY + ), vec![ - 149, 109, 253, 20, 158, 39, 112, 199, 28, 74, 3, 106, 99, 8, 234, 59, 64, 172, 126, - 5, 155, 28, 59, 21, 220, 196, 241, 54, 138, 5, 71, 107 + 149, 109, 253, 20, 158, 39, 112, 199, 28, 74, 3, 106, 99, 8, 234, + 59, 64, 172, 126, 5, 155, 28, 59, 21, 220, 196, 241, 54, 138, 5, 71, + 107 ] .as_slice() ); @@ -195,8 +212,9 @@ mod tests { KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY ), vec![ - 243, 80, 193, 249, 151, 10, 93, 168, 117, 239, 214, 89, 161, 130, 122, 81, 238, - 177, 51, 113, 21, 74, 73, 212, 199, 41, 75, 155, 49, 55, 217, 226 + 243, 80, 193, 249, 151, 10, 93, 168, 117, 239, 214, 89, 161, 130, + 122, 81, 238, 177, 51, 113, 21, 74, 73, 212, 199, 41, 75, 155, 49, + 55, 217, 226 ] .as_slice() ); diff --git a/clash_lib/src/proxy/vmess/vmess_impl/mod.rs b/clash_lib/src/proxy/vmess/vmess_impl/mod.rs index 0b98ba4a0..47d39f75a 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/mod.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/mod.rs @@ -1,7 +1,7 @@ mod cipher; mod client; mod header; -//pub mod http; +// pub mod http; mod datagram; mod kdf; mod stream; @@ -25,6 +25,5 @@ pub(crate) const COMMAND_UDP: u8 = 2; const CHUNK_SIZE: usize = 1 << 14; const MAX_CHUNK_SIZE: usize = 17 * 1024; -pub use client::Builder; -pub use client::VmessOption; +pub use client::{Builder, VmessOption}; pub use datagram::OutboundDatagramVmess; diff --git a/clash_lib/src/proxy/vmess/vmess_impl/stream.rs b/clash_lib/src/proxy/vmess/vmess_impl/stream.rs index 41470ff9d..87b81bc61 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/stream.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/stream.rs @@ -22,12 +22,14 @@ use super::{ cipher::{AeadCipher, VmessSecurity}, header, kdf::{ - self, KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, - KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV, KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, + self, KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, + KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, + KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV, + KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, }, user::ID, - Security, CHUNK_SIZE, COMMAND_TCP, COMMAND_UDP, OPTION_CHUNK_STREAM, SECURITY_AES_128_GCM, - SECURITY_CHACHA20_POLY1305, SECURITY_NONE, VERSION, + Security, CHUNK_SIZE, COMMAND_TCP, COMMAND_UDP, OPTION_CHUNK_STREAM, + SECURITY_AES_128_GCM, SECURITY_CHACHA20_POLY1305, SECURITY_NONE, VERSION, }; pub struct VmessStream { @@ -158,11 +160,13 @@ where let (aead_read_cipher, aead_write_cipher) = match *security { SECURITY_NONE => (None, None), SECURITY_AES_128_GCM => { - let write_cipher = - VmessSecurity::Aes128Gcm(Aes128Gcm::new_with_slice(&req_body_key)); + let write_cipher = VmessSecurity::Aes128Gcm( + Aes128Gcm::new_with_slice(&req_body_key), + ); let write_cipher = AeadCipher::new(&req_body_iv, write_cipher); - let reader_cipher = - VmessSecurity::Aes128Gcm(Aes128Gcm::new_with_slice(&resp_body_key)); + let reader_cipher = VmessSecurity::Aes128Gcm( + Aes128Gcm::new_with_slice(&resp_body_key), + ); let read_cipher = AeadCipher::new(&resp_body_iv, reader_cipher); (Some(read_cipher), Some(write_cipher)) } @@ -172,8 +176,9 @@ where let tmp = utils::md5(&key[..16]); key[16..].copy_from_slice(&tmp); - let write_cipher = - VmessSecurity::ChaCha20Poly1305(ChaCha20Poly1305::new_with_slice(&key)); + let write_cipher = VmessSecurity::ChaCha20Poly1305( + ChaCha20Poly1305::new_with_slice(&key), + ); let write_cipher = AeadCipher::new(&req_body_iv, write_cipher); let mut key = [0u8; 32]; @@ -181,8 +186,9 @@ where let tmp = utils::md5(&key[..16]); key[16..].copy_from_slice(&tmp); - let reader_cipher = - VmessSecurity::ChaCha20Poly1305(ChaCha20Poly1305::new_with_slice(&key)); + let reader_cipher = VmessSecurity::ChaCha20Poly1305( + ChaCha20Poly1305::new_with_slice(&key), + ); let read_cipher = AeadCipher::new(&resp_body_iv, reader_cipher); (Some(read_cipher), Some(write_cipher)) @@ -252,8 +258,8 @@ where let mut mbuf = BytesMut::new(); if !is_aead { - let mut mac = - HmacMd5::new_from_slice(id.uuid.as_bytes()).expect("key len expected to be 16"); + let mut mac = HmacMd5::new_from_slice(id.uuid.as_bytes()) + .expect("key len expected to be 16"); mac.update(now.to_be_bytes().as_slice()); mbuf.put_slice(&mac.finalize().into_bytes()); } @@ -289,15 +295,23 @@ where if !is_aead { let mut data = buf.to_vec(); - crypto::aes_cfb_encrypt(&id.cmd_key[..], &hash_timestamp(now)[..], &mut data) - .map_err(map_io_error)?; + crypto::aes_cfb_encrypt( + &id.cmd_key[..], + &hash_timestamp(now)[..], + &mut data, + ) + .map_err(map_io_error)?; mbuf.put_slice(data.as_slice()); let out = mbuf.freeze(); stream.write_all(&out).await?; } else { - let out = header::seal_vmess_aead_header(id.cmd_key, buf.freeze().to_vec(), now) - .map_err(map_io_error)?; + let out = header::seal_vmess_aead_header( + id.cmd_key, + buf.freeze().to_vec(), + now, + ) + .map_err(map_io_error)?; stream.write_all(&out).await?; } @@ -327,8 +341,12 @@ where if !this.is_aead { ready!(this.poll_read_exact(cx, 4))?; let mut buf = this.read_buf.split().freeze().to_vec(); - crypto::aes_cfb_decrypt(&resp_body_key, &resp_body_iv, &mut buf) - .map_err(map_io_error)?; + crypto::aes_cfb_decrypt( + &resp_body_key, + &resp_body_iv, + &mut buf, + ) + .map_err(map_io_error)?; if buf[0] != resp_v { return Poll::Ready(Err(std::io::Error::new( std::io::ErrorKind::InvalidData, @@ -347,14 +365,16 @@ where } else { ready!(this.poll_read_exact(cx, 18))?; - let aead_response_header_length_encryption_key = &kdf::vmess_kdf_1_one_shot( - &resp_body_key, - KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, - )[..16]; - let aead_response_header_length_encryption_iv = &kdf::vmess_kdf_1_one_shot( - &resp_body_iv, - KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, - )[..12]; + let aead_response_header_length_encryption_key = + &kdf::vmess_kdf_1_one_shot( + &resp_body_key, + KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_KEY, + )[..16]; + let aead_response_header_length_encryption_iv = + &kdf::vmess_kdf_1_one_shot( + &resp_body_iv, + KDF_SALT_CONST_AEAD_RESP_HEADER_LEN_IV, + )[..12]; let decrypted_response_header_len = crypto::aes_gcm_decrypt( aead_response_header_length_encryption_key, @@ -372,10 +392,13 @@ where .into(); } - this.read_state = ReadState::AeadWaitingHeader(u16::from_be_bytes( - decrypted_response_header_len[..2].try_into().unwrap(), - ) - as usize); + this.read_state = ReadState::AeadWaitingHeader( + u16::from_be_bytes( + decrypted_response_header_len[..2] + .try_into() + .unwrap(), + ) as usize, + ); } } @@ -386,14 +409,16 @@ where let resp_body_key = this.resp_body_key.clone(); let resp_body_iv = this.resp_body_iv.clone(); - let aead_response_header_payload_encryption_key = &kdf::vmess_kdf_1_one_shot( - &resp_body_key, - KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, - )[..16]; - let aead_response_header_payload_encryption_iv = &kdf::vmess_kdf_1_one_shot( - &resp_body_iv, - KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV, - )[..12]; + let aead_response_header_payload_encryption_key = + &kdf::vmess_kdf_1_one_shot( + &resp_body_key, + KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_KEY, + )[..16]; + let aead_response_header_payload_encryption_iv = + &kdf::vmess_kdf_1_one_shot( + &resp_body_iv, + KDF_SALT_CONST_AEAD_RESP_HEADER_PAYLOAD_IV, + )[..12]; let buf = crypto::aes_gcm_decrypt( aead_response_header_payload_encryption_key, @@ -430,8 +455,9 @@ where ReadState::StreamWaitingLength => { let this = &mut *self; ready!(this.poll_read_exact(cx, 2))?; - let len = u16::from_be_bytes(this.read_buf.split().as_ref().try_into().unwrap()) - as usize; + let len = u16::from_be_bytes( + this.read_buf.split().as_ref().try_into().unwrap(), + ) as usize; if len > MAX_CHUNK_SIZE { return Poll::Ready(Err(std::io::Error::new( @@ -463,7 +489,8 @@ where buf.put_slice(&payload); if to_read < size { // there're unread data, continues in next poll - self.read_state = ReadState::StreamFlushingData(size - to_read); + self.read_state = + ReadState::StreamFlushingData(size - to_read); } else { // all data consumed, ready to read next chunk self.read_state = ReadState::StreamWaitingLength; @@ -506,26 +533,31 @@ where piece2.put_slice(&buf[..consume_len]); if let Some(ref mut cipher) = this.aead_write_cipher { - piece2 - .extend_from_slice(vec![0u8; cipher.security.overhead_len()].as_ref()); + piece2.extend_from_slice( + vec![0u8; cipher.security.overhead_len()].as_ref(), + ); cipher.encrypt_inplace(&mut piece2)?; } this.write_buf.unsplit(piece2); // ready to write data - self.write_state = - WriteState::FlushingData(consume_len, (this.write_buf.len(), 0)); + self.write_state = WriteState::FlushingData( + consume_len, + (this.write_buf.len(), 0), + ); } - // consumed is the consumed plaintext length we're going to return to caller. - // total is total length of the ciphertext data chunk we're going to write to remote. + // consumed is the consumed plaintext length we're going to + // return to caller. total is total length of + // the ciphertext data chunk we're going to write to remote. // written is the number of ciphertext bytes were written. WriteState::FlushingData(consumed, (total, written)) => { let this = &mut *self; - // There would be trouble if the caller change the buf upon pending, but I - // believe that's not a usual use case. + // There would be trouble if the caller change the buf upon + // pending, but I believe that's not a + // usual use case. let nw = ready!(tokio_util::io::poll_write_buf( Pin::new(&mut this.stream), cx, @@ -545,7 +577,8 @@ where return Poll::Ready(Ok(consumed)); } - this.write_state = WriteState::FlushingData(consumed, (total, written + nw)); + this.write_state = + WriteState::FlushingData(consumed, (total, written + nw)); } } } diff --git a/clash_lib/src/proxy/vmess/vmess_impl/user.rs b/clash_lib/src/proxy/vmess/vmess_impl/user.rs index ae9a0d912..4de74c62a 100644 --- a/clash_lib/src/proxy/vmess/vmess_impl/user.rs +++ b/clash_lib/src/proxy/vmess/vmess_impl/user.rs @@ -46,19 +46,24 @@ fn next_id(i: &uuid::Uuid) -> uuid::Uuid { mod tests { #[test] fn test_new_id() { - let id = - super::new_id(&uuid::Uuid::parse_str("b831381d-6324-4d53-ad4f-8cda48b30811").unwrap()); + let id = super::new_id( + &uuid::Uuid::parse_str("b831381d-6324-4d53-ad4f-8cda48b30811").unwrap(), + ); assert_eq!(id.uuid.to_string(), "b831381d-6324-4d53-ad4f-8cda48b30811"); assert_eq!( id.cmd_key, - [181, 13, 145, 106, 192, 206, 192, 103, 152, 26, 248, 229, 243, 138, 117, 143] + [ + 181, 13, 145, 106, 192, 206, 192, 103, 152, 26, 248, 229, 243, 138, + 117, 143 + ] ); } #[test] fn test_next_id() { - let id = - super::new_id(&uuid::Uuid::parse_str("b831381d-6324-4d53-ad4f-8cda48b30811").unwrap()); + let id = super::new_id( + &uuid::Uuid::parse_str("b831381d-6324-4d53-ad4f-8cda48b30811").unwrap(), + ); let next_id = super::next_id(&id.uuid); assert_eq!(next_id.to_string(), "5a071834-12d5-980a-72ac-845d5568d17d"); } diff --git a/clash_lib/src/proxy/wg/device.rs b/clash_lib/src/proxy/wg/device.rs index 32d853411..1bb1e0b35 100644 --- a/clash_lib/src/proxy/wg/device.rs +++ b/clash_lib/src/proxy/wg/device.rs @@ -25,7 +25,9 @@ use tokio::sync::{ }; use tracing::{debug, error, trace, trace_span, warn, Instrument}; -use crate::{app::dns::ThreadSafeDNSResolver, proxy::datagram::UdpPacket, session::SocksAddr}; +use crate::{ + app::dns::ThreadSafeDNSResolver, proxy::datagram::UdpPacket, session::SocksAddr, +}; use super::{ events::PortProtocol, @@ -89,7 +91,8 @@ impl DeviceManager { let tcp_port_pool = PortPool::new(); let udp_port_pool = PortPool::new(); - let (socket_notifier, socket_notifier_receiver) = tokio::sync::mpsc::channel(1024); + let (socket_notifier, socket_notifier_receiver) = + tokio::sync::mpsc::channel(1024); Self { addr, @@ -135,7 +138,11 @@ impl DeviceManager { UdpPair::new(read_pair.1, write_pair.0) } - pub async fn look_up_dns(&self, host: &str, server: SocketAddr) -> Option { + pub async fn look_up_dns( + &self, + host: &str, + server: SocketAddr, + ) -> Option { debug!("looking up {} on {}", host, server); #[async_recursion::async_recursion] @@ -160,19 +167,26 @@ impl DeviceManager { msg.set_recursion_desired(true); - let pkt = UdpPacket::new(msg.to_vec().unwrap(), SocksAddr::any_ipv4(), server.into()); + let pkt = UdpPacket::new( + msg.to_vec().unwrap(), + SocksAddr::any_ipv4(), + server.into(), + ); socket.feed(pkt).await.ok()?; socket.flush().await.ok()?; trace!("sent dns query: {:?}", msg); - let pkt = match tokio::time::timeout(Duration::from_secs(5), socket.next()).await { - Ok(Some(pkt)) => pkt, - _ => { - warn!("wg dns query timed out with server {server}"); - return None; - } - }; + let pkt = + match tokio::time::timeout(Duration::from_secs(5), socket.next()) + .await + { + Ok(Some(pkt)) => pkt, + _ => { + warn!("wg dns query timed out with server {server}"); + return None; + } + }; let msg = hickory_proto::op::Message::from_vec(&pkt.data).ok()?; trace!("got dns response: {:?}", msg); @@ -185,7 +199,13 @@ impl DeviceManager { "{} resolved to CNAME {}, asking recursively", host, cname.0 ); - return query(rtype, &cname.0.to_ascii(), server, socket).await; + return query( + rtype, + &cname.0.to_ascii(), + server, + socket, + ) + .await; } ( hickory_proto::rr::RecordType::A, @@ -211,7 +231,8 @@ impl DeviceManager { let v4_query = query(hickory_proto::rr::RecordType::A, host, server, socket); if self.addr_v6.is_some() { let socket = self.new_udp_socket().await; - let v6_query = query(hickory_proto::rr::RecordType::AAAA, host, server, socket); + let v6_query = + query(hickory_proto::rr::RecordType::AAAA, host, server, socket); match tokio::time::timeout( Duration::from_secs(5), futures::future::join(v4_query, v6_query), @@ -247,8 +268,10 @@ impl DeviceManager { let (device_sender, mut device_receiver) = tokio::sync::mpsc::channel(1024); - let mut tcp_queue: HashMap> = HashMap::new(); - let mut udp_queue: HashMap> = HashMap::new(); + let mut tcp_queue: HashMap> = + HashMap::new(); + let mut udp_queue: HashMap> = + HashMap::new(); let mut next_poll = None; loop { @@ -256,7 +279,8 @@ impl DeviceManager { let mut socket_pairs = self.socket_pairs.lock().await; let mut packet_notifier = self.packet_notifier.lock().await; - let mut socket_notifier_receiver = self.socket_notifier_receiver.lock().await; + let mut socket_notifier_receiver = + self.socket_notifier_receiver.lock().await; tokio::select! { Some(socket) = socket_notifier_receiver.recv() => { @@ -633,16 +657,20 @@ impl VirtualIpDevice { // when wg stack receives a packet, it will send it to this receiver mut packet_receiver: Receiver<(PortProtocol, Bytes)>, - // when wg stack receives a packet, it will send a notification to this sender + // when wg stack receives a packet, it will send a notification to this + // sender packet_notifier: Sender<()>, mtu: usize, ) -> Self { - let (inner_packet_sender, inner_packet_receiver) = tokio::sync::mpsc::channel(1024); + let (inner_packet_sender, inner_packet_receiver) = + tokio::sync::mpsc::channel(1024); tokio::spawn(async move { loop { let span = trace_span!("receive_packet"); - if let Some((proto, data)) = packet_receiver.recv().instrument(span).await { + if let Some((proto, data)) = + packet_receiver.recv().instrument(span).await + { inner_packet_sender.send((proto, data)).await.unwrap(); let _ = packet_notifier.try_send(()); } else { @@ -686,7 +714,10 @@ impl Device for VirtualIpDevice { } } - fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + fn transmit( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option> { Some(TxToken { sender: self.packet_sender.clone(), }) diff --git a/clash_lib/src/proxy/wg/mod.rs b/clash_lib/src/proxy/wg/mod.rs index 1eb63eefa..ff3bda584 100644 --- a/clash_lib/src/proxy/wg/mod.rs +++ b/clash_lib/src/proxy/wg/mod.rs @@ -4,19 +4,17 @@ use std::{ sync::Arc, }; -use crate::{ - app::dispatcher::{ - ChainedDatagram, ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, - }, - Error, -}; use crate::{ app::{ - dispatcher::{BoxedChainedDatagram, BoxedChainedStream}, + dispatcher::{ + BoxedChainedDatagram, BoxedChainedStream, ChainedDatagram, + ChainedDatagramWrapper, ChainedStream, ChainedStreamWrapper, + }, dns::ThreadSafeDNSResolver, }, common::errors::{map_io_error, new_io_error}, session::Session, + Error, }; use self::{keys::KeyBytes, wireguard::Config}; @@ -77,7 +75,10 @@ impl Handler { }) } - async fn initialize_inner(&self, resolver: ThreadSafeDNSResolver) -> Result<&Inner, Error> { + async fn initialize_inner( + &self, + resolver: ThreadSafeDNSResolver, + ) -> Result<&Inner, Error> { self.inner .get_or_try_init(|| async { let recv_pair = tokio::sync::mpsc::channel(1024); @@ -87,7 +88,8 @@ impl Handler { .await .map_err(map_io_error)? .ok_or(new_io_error( - format!("invalid remote server: {}", self.opts.server).as_str(), + format!("invalid remote server: {}", self.opts.server) + .as_str(), ))?; let allowed_ips = self .opts @@ -97,7 +99,10 @@ impl Handler { ips.iter() .map(|ip| { ip.parse::().map_err(|e| { - new_io_error(format!("invalid allowed ip: {}", e).as_str()) + new_io_error( + format!("invalid allowed ip: {}", e) + .as_str(), + ) }) }) .collect::, _>>() @@ -108,7 +113,13 @@ impl Handler { // we shouldn't create a new tunnel for each connection let wg = wireguard::WireguardTunnel::new( Config { - private_key: self.opts.private_key.parse::().unwrap().0.into(), + private_key: self + .opts + .private_key + .parse::() + .unwrap() + .0 + .into(), endpoint_public_key: self .opts .public_key @@ -168,7 +179,9 @@ impl Handler { .map(|server| { server .iter() - .map(|s| (s.parse::().unwrap(), 53).into()) + .map(|s| { + (s.parse::().unwrap(), 53).into() + }) .collect::>() }) .unwrap_or_default() @@ -283,10 +296,14 @@ impl OutboundHandler for Handler { #[cfg(all(test, not(ci)))] mod tests { - use crate::proxy::utils::test_utils::docker_runner::DockerTestRunnerBuilder; - use crate::proxy::utils::test_utils::{config_helper::test_config_base_dir, Suite}; + use crate::proxy::utils::test_utils::{ + config_helper::test_config_base_dir, docker_runner::DockerTestRunnerBuilder, + Suite, + }; - use super::super::utils::test_utils::{consts::*, docker_runner::DockerTestRunner}; + use super::super::utils::test_utils::{ + consts::*, docker_runner::DockerTestRunner, + }; use crate::proxy::utils::test_utils::run_test_suites_and_cleanup; use super::*; @@ -297,7 +314,8 @@ mod tests { async fn get_runner() -> anyhow::Result { let test_config_dir = test_config_base_dir(); let wg_config = test_config_dir.join("wg_config"); - // the following configs is in accordance with the config in `wg_config` dir + // the following configs is in accordance with the config in `wg_config` + // dir DockerTestRunnerBuilder::new() .image(IMAGE_WG) .env(&[ @@ -330,7 +348,9 @@ mod tests { ipv6: None, private_key: "KIlDUePHyYwzjgn18przw/ZwPioJhh2aEyhxb/dtCXI=".to_owned(), public_key: "INBZyvB715sA5zatkiX8Jn3Dh5tZZboZ09x4pkr66ig=".to_owned(), - preshared_key: Some("+JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=".to_owned()), + preshared_key: Some( + "+JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=".to_owned(), + ), remote_dns_resolve: false, dns: None, mtu: Some(1000), @@ -340,12 +360,18 @@ mod tests { }; let handler = Handler::new(opts); - // cannot run the ping pong test, since the wireguard server is running on bridge network mode - // and the `net.ipv4.conf.all.src_valid_mark` is not supported in the host network mode - // the latency test should be enough + // cannot run the ping pong test, since the wireguard server is running + // on bridge network mode and the `net.ipv4.conf.all. + // src_valid_mark` is not supported in the host network mode the + // latency test should be enough let runner = get_runner().await?; // FIXME: wait for the startup of the test runner in a more elegant way tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - run_test_suites_and_cleanup(handler, runner, &[Suite::LatencyTcp, Suite::DnsUdp]).await + run_test_suites_and_cleanup( + handler, + runner, + &[Suite::LatencyTcp, Suite::DnsUdp], + ) + .await } } diff --git a/clash_lib/src/proxy/wg/ports.rs b/clash_lib/src/proxy/wg/ports.rs index d6a90ddf9..75499dcb6 100644 --- a/clash_lib/src/proxy/wg/ports.rs +++ b/clash_lib/src/proxy/wg/ports.rs @@ -33,7 +33,8 @@ impl PortPool { } } - /// Requests a free port from the pool. An error is returned if none is available (exhausted max capacity). + /// Requests a free port from the pool. An error is returned if none is + /// available (exhausted max capacity). pub async fn next(&self) -> anyhow::Result { let mut inner = self.inner.write().await; let port = inner diff --git a/clash_lib/src/proxy/wg/stack/udp.rs b/clash_lib/src/proxy/wg/stack/udp.rs index 5823db722..316418a70 100644 --- a/clash_lib/src/proxy/wg/stack/udp.rs +++ b/clash_lib/src/proxy/wg/stack/udp.rs @@ -51,7 +51,10 @@ impl Sink for UdpPair { } } - fn start_send(self: std::pin::Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + fn start_send( + self: std::pin::Pin<&mut Self>, + item: UdpPacket, + ) -> Result<(), Self::Error> { let this = self.get_mut(); this.pkt = Some(item); this.flushed = false; diff --git a/clash_lib/src/proxy/wg/wireguard.rs b/clash_lib/src/proxy/wg/wireguard.rs index 2a1b51cee..8f3428afe 100644 --- a/clash_lib/src/proxy/wg/wireguard.rs +++ b/clash_lib/src/proxy/wg/wireguard.rs @@ -238,7 +238,11 @@ impl WireguardTunnel { match self.udp_send(packet).await { Ok(_) => {} Err(e) => { - error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {:?}", e); + error!( + "Failed to send decapsulation-instructed \ + packet to WireGuard endpoint: {:?}", + e + ); break; } }; @@ -303,7 +307,10 @@ impl WireguardTunnel { } #[async_recursion] - async fn handle_routine_result<'a: 'async_recursion>(&self, result: TunnResult<'a>) { + async fn handle_routine_result<'a: 'async_recursion>( + &self, + result: TunnResult<'a>, + ) { match result { TunnResult::Done => { tokio::time::sleep(Duration::from_millis(100)).await; @@ -312,7 +319,8 @@ impl WireguardTunnel { warn!("wireguard connection expired"); let mut buf = vec![0u8; 65535]; let mut peer = self.peer.lock().await; - let tun_result = peer.format_handshake_initiation(&mut buf[..], false); + let tun_result = + peer.format_handshake_initiation(&mut buf[..], false); drop(peer); self.handle_routine_result(tun_result).await; @@ -320,12 +328,14 @@ impl WireguardTunnel { TunnResult::Err(e) => { error!("wireguard error: {e:?}"); } - TunnResult::WriteToNetwork(packet) => match self.udp_send(packet).await { - Ok(_) => {} - Err(e) => { - error!("failed to send packet: {}", e); + TunnResult::WriteToNetwork(packet) => { + match self.udp_send(packet).await { + Ok(_) => {} + Err(e) => { + error!("failed to send packet: {}", e); + } } - }, + } _ => { error!("unexpected result from wireguard"); } @@ -338,23 +348,29 @@ impl WireguardTunnel { match IpVersion::of_packet(packet) { Ok(IpVersion::Ipv4) => Ipv4Packet::new_checked(&packet) .ok() - .filter(|packet| Ipv4Addr::from(packet.dst_addr()) == self.source_peer_ip) + .filter(|packet| { + Ipv4Addr::from(packet.dst_addr()) == self.source_peer_ip + }) .and_then(|packet| { match packet.next_header() { IpProtocol::Tcp => Some(PortProtocol::Tcp), IpProtocol::Udp => Some(PortProtocol::Udp), - // Unrecognized protocol, so we cannot determine where to route + // Unrecognized protocol, so we cannot determine where + // to route _ => None, } }), Ok(IpVersion::Ipv6) => Ipv6Packet::new_checked(&packet) .ok() - .filter(|packet| Some(Ipv6Addr::from(packet.dst_addr())) == self.source_peer_ipv6) + .filter(|packet| { + Some(Ipv6Addr::from(packet.dst_addr())) == self.source_peer_ipv6 + }) .and_then(|packet| { match packet.next_header() { IpProtocol::Tcp => Some(PortProtocol::Tcp), IpProtocol::Udp => Some(PortProtocol::Udp), - // Unrecognized protocol, so we cannot determine where to route + // Unrecognized protocol, so we cannot determine where + // to route _ => None, } }), @@ -364,7 +380,8 @@ impl WireguardTunnel { fn is_ip_allowed(&self, ip: IpAddr) -> bool { trace!("checking if {} is allowed in {:?}", ip, self.allowed_ips); - self.allowed_ips.is_empty() || self.allowed_ips.iter().any(|x| x.contains(&ip)) + self.allowed_ips.is_empty() + || self.allowed_ips.iter().any(|x| x.contains(&ip)) } } diff --git a/clash_lib/src/session.rs b/clash_lib/src/session.rs index ac9725a48..88162d3f0 100644 --- a/clash_lib/src/session.rs +++ b/clash_lib/src/session.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; -use std::fmt::{Debug, Display, Formatter}; use std::{ + collections::HashMap, + fmt::{Debug, Display, Formatter}, io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, }; @@ -34,8 +34,8 @@ impl Display for SocksAddr { pub struct SocksAddrType; impl SocksAddrType { - pub const V4: u8 = 0x1; pub const DOMAIN: u8 = 0x3; + pub const V4: u8 = 0x1; pub const V6: u8 = 0x4; } @@ -97,7 +97,7 @@ impl SocksAddr { pub fn is_domain(&self) -> bool { match self { SocksAddr::Ip(_) => false, - SocksAddr::Domain(_, _) => true, + SocksAddr::Domain(..) => true, } } @@ -111,7 +111,7 @@ impl SocksAddr { pub fn must_into_socket_addr(self) -> SocketAddr { match self { SocksAddr::Ip(addr) => addr, - SocksAddr::Domain(_, _) => panic!("not a socket address"), + SocksAddr::Domain(..) => panic!("not a socket address"), } } @@ -185,7 +185,8 @@ impl SocksAddr { let mut buf = vec![0u8; domain_len]; cur.copy_to_slice(&mut buf); let port = cur.get_u16(); - let domain_name = String::from_utf8(buf).map_err(|_x| invalid_domain())?; + let domain_name = + String::from_utf8(buf).map_err(|_x| invalid_domain())?; Ok(Self::Domain(domain_name, port)) } _ => Err(invalid_atyp()), @@ -227,7 +228,9 @@ impl Clone for SocksAddr { fn clone(&self) -> Self { match self { SocksAddr::Ip(a) => Self::from(a.to_owned()), - SocksAddr::Domain(domain, port) => Self::try_from((domain.clone(), *port)).unwrap(), + SocksAddr::Domain(domain, port) => { + Self::try_from((domain.clone(), *port)).unwrap() + } } } } @@ -317,9 +320,13 @@ impl TryFrom<&[u8]> for SocksAddr { if buf.len() < 1 + domain_len + 2 { return Err(insuff_bytes()); } - let domain = String::from_utf8((buf[2..domain_len + 2]).to_vec()).map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("invalid domain: {}", e)) - })?; + let domain = String::from_utf8((buf[2..domain_len + 2]).to_vec()) + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("invalid domain: {}", e), + ) + })?; let mut port_bytes = [0u8; 2]; (port_bytes).copy_from_slice(&buf[domain_len + 2..domain_len + 4]); let port = u16::from_be_bytes(port_bytes); @@ -337,7 +344,9 @@ impl TryFrom for SocketAddr { fn try_from(s: SocksAddr) -> Result { match s { SocksAddr::Ip(ip) => Ok(ip), - SocksAddr::Domain(_, _) => Err(io::Error::new(io::ErrorKind::Other, "cannot convert")), + SocksAddr::Domain(..) => { + Err(io::Error::new(io::ErrorKind::Other, "cannot convert")) + } } } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..056190ea2 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,17 @@ +edition = "2021" +reorder_modules = true +reorder_impl_items = true +reorder_imports = true +format_strings = true +format_code_in_doc_comments = true +format_macro_matchers = true +condense_wildcard_suffixes = true +normalize_comments = true +use_try_shorthand = true +wrap_comments = true +use_field_init_shorthand = true +error_on_line_overflow = false +tab_spaces = 4 +enum_discrim_align_threshold = 20 +max_width = 85 +imports_granularity = "Crate"