diff --git a/Cargo.lock b/Cargo.lock index acd5256..54fb9e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" +checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" [[package]] name = "rustls-webpki" diff --git a/Cargo.toml b/Cargo.toml index c26d76b..8de22de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ ringbuf = "0.3" httparse = "1.8" async_smoltcp = { path = "async_smoltcp" } tokio-rustls = "0.25" -rustls-pki-types = "1.1" +rustls-pki-types = "1.2" futures = "0.3" [dev-dependencies] diff --git a/async_smoltcp/src/device.rs b/async_smoltcp/src/device.rs index c398471..ed48671 100644 --- a/async_smoltcp/src/device.rs +++ b/async_smoltcp/src/device.rs @@ -93,8 +93,9 @@ fn is_private_v4(addr: IpAddress) -> bool { } impl<'a, T: Tun + Clone> TunDevice<'a, T> { - pub fn new(mtu: usize, tun: T) -> Self { + pub fn new(tun: T) -> Self { let channel_buffer = 1024; + let mtu = tun.mtu(); let (tcp_sender, tcp_receiver) = channel(channel_buffer); let (udp_sender, udp_receiver) = channel(channel_buffer); let mut device = Self { diff --git a/async_smoltcp/src/lib.rs b/async_smoltcp/src/lib.rs index 70aaab4..53fbda0 100644 --- a/async_smoltcp/src/lib.rs +++ b/async_smoltcp/src/lib.rs @@ -25,6 +25,9 @@ pub trait Tun { /// Allocate a packet which can hold len bytes data. fn allocate_packet(&self, len: usize) -> std::io::Result; + + /// Get the MTU of the tun device. + fn mtu(&self) -> usize; } impl Tun for Arc @@ -41,6 +44,9 @@ where fn allocate_packet(&self, len: usize) -> std::io::Result { self.deref().allocate_packet(len) } + fn mtu(&self) -> usize { + self.deref().mtu() + } } pub trait Packet { diff --git a/mobile2/backend/Cargo.lock b/mobile2/backend/Cargo.lock index f2394bf..75541e3 100644 --- a/mobile2/backend/Cargo.lock +++ b/mobile2/backend/Cargo.lock @@ -227,7 +227,7 @@ version = "0.1.0" dependencies = [ "bytes", "log", - "smoltcp 0.11.0", + "smoltcp", "tokio", "tokio-util", ] @@ -255,15 +255,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -562,12 +553,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crossbeam" version = "0.8.2" @@ -1373,15 +1358,6 @@ dependencies = [ "syn 2.0.42", ] -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - [[package]] name = "hash32" version = "0.3.1" @@ -1403,26 +1379,13 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32 0.2.1", - "rustc_version", - "spin", - "stable_deref_trait", -] - [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "hash32 0.3.1", + "hash32", "stable_deref_trait", ] @@ -1794,10 +1757,11 @@ dependencies = [ "paste", "rayon", "rustls", + "rustls-pki-types", "serde", "serde_json", "sha2", - "smoltcp 0.10.0", + "smoltcp", "socket2 0.5.5", "tokio", "tokio-rustls", @@ -2500,9 +2464,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" +checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" [[package]] name = "rustls-webpki" @@ -2681,22 +2645,6 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "smoltcp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2e3a36ac8fea7b94e666dfa3871063d6e0a5c9d5d4fec9a1a6b7b6760f0229" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "cfg-if", - "defmt", - "heapless 0.7.17", - "libc", - "log", - "managed", -] - [[package]] name = "smoltcp" version = "0.11.0" @@ -2707,7 +2655,7 @@ dependencies = [ "byteorder", "cfg-if", "defmt", - "heapless 0.8.0", + "heapless", "libc", "log", "managed", @@ -2764,9 +2712,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "stable_deref_trait" diff --git a/mobile2/backend/Cargo.toml b/mobile2/backend/Cargo.toml index 8746ee7..6090a67 100644 --- a/mobile2/backend/Cargo.toml +++ b/mobile2/backend/Cargo.toml @@ -40,8 +40,9 @@ serde_json = "1.0" serde = "1.0" lazy_static = "1.4" derive_more = "0.99" -smoltcp = "0.10" +smoltcp = "0.11.0" rustls = { version = "0.22", features = [] } +rustls-pki-types = "1.2" itertools = "0.12.0" bytes = "1.5" crossbeam = "0.8" diff --git a/mobile2/backend/gen/android/.idea/vcs.xml b/mobile2/backend/gen/android/.idea/vcs.xml index 423963a..4fce1d8 100644 --- a/mobile2/backend/gen/android/.idea/vcs.xml +++ b/mobile2/backend/gen/android/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/mobile2/backend/src/tun/mod.rs b/mobile2/backend/src/tun/mod.rs index 87d68c0..6dd0da0 100644 --- a/mobile2/backend/src/tun/mod.rs +++ b/mobile2/backend/src/tun/mod.rs @@ -1,5 +1,9 @@ use std::{ - net::{IpAddr, SocketAddr}, + fs::File, + io::{ErrorKind, Read, Write}, + mem::ManuallyDrop, + net::SocketAddr, + ops::Deref, str::FromStr, sync::{ atomic::{AtomicBool, Ordering}, @@ -9,130 +13,136 @@ use std::{ }; use bytes::BytesMut; -use rustls::{ClientConfig, ClientConnection, OwnedTrustAnchor, RootCertStore, ServerName}; +use rustls::{pki_types::ServerName, ClientConfig, RootCertStore}; use sha2::{Digest, Sha224}; -use tokio::{net::UdpSocket, runtime::Builder, spawn, sync::mpsc::channel}; -use trust_dns_proto::{ - op::{Message, Query}, - rr::{DNSClass, Name, RecordType}, - serialize::binary::BinDecodable, -}; +use tokio::{net::TcpStream, runtime::Builder, spawn, sync::mpsc::channel}; +use tokio_rustls::{client::TlsStream, TlsConnector}; +use trust_dns_proto::serialize::binary::BinDecodable; -use async_smoltcp::TunDevice; +use async_smoltcp::{Packet as _, TunDevice}; -use crate::{ - emit_event, platform, - tun::{ - dns::start_dns, - proto::{TrojanRequest, UDP_ASSOCIATE}, - tcp::start_tcp, - udp::{run_udp_dispatch, start_udp}, - }, - types, - types::{EventType, VpnError}, - Context, -}; +use crate::{platform, types, types::Error, LOOPER}; mod dns; mod proto; mod tcp; mod udp; -pub async fn init_tls_conn( - config: Arc, - server_addr: SocketAddr, - server_name: ServerName, -) -> types::Result { - let stream = tokio::net::TcpStream::connect(server_addr).await?; - let session = ClientConnection::new(config, server_name)?; - Ok(TlsClientStream::new(stream, session)) +pub struct Session { + mtu: usize, + file: ManuallyDrop, } -fn digest_pass(pass: &String) -> String { - let mut encoder = Sha224::new(); - encoder.update(pass.as_bytes()); - let result = encoder.finalize(); - hex::encode(result.as_slice()) -} +impl Session { + pub fn new(fd: i32) -> types::Result { + let mtu = LOOPER + .read() + .map_err(|err| Error::Lock(err.to_string()))? + .config + .mtu; + Ok(Self { + mtu, + file: ManuallyDrop::new(File::from_raw_fd(fd)), + }) + } -/// This function resolves a domain name to a list of IP addresses. -pub async fn resolve(name: &str, dns_server_addr: &str) -> types::Result> { - let dns_server_addr: SocketAddr = dns_server_addr.parse()?; - let socket = UdpSocket::bind("0.0.0.0:0").await?; - let mut message = Message::new(); - message.set_recursion_desired(true); - message.set_id(1); - let mut query = Query::new(); - let name = Name::from_str(name)?; - query.set_name(name); - query.set_query_type(RecordType::A); - query.set_query_class(DNSClass::IN); - message.add_query(query); - let request = message.to_vec()?; - if request.len() != socket.send_to(request.as_slice(), dns_server_addr).await? { - log::error!("send dns query to server failed"); - return Err(VpnError::Resolve); + pub fn mtu(&self) -> usize { + self.mtu } - let mut response = vec![0u8; 1024]; - tokio::select! { - _ = tokio::time::sleep(Duration::from_secs(5)) => { - log::error!("dns query timeout"); - Err(VpnError::Resolve) +} + +pub struct Packet { + data: Vec, +} + +impl Packet { + pub fn new(mtu: usize) -> Self { + Self { + data: vec![0u8; mtu], } - ret = socket.recv(response.as_mut_slice()) => { - let length = ret?; - let message = Message::from_bytes(&response.as_slice()[..length])?; - if message.id() != 1 { - log::error!("dns response id not match"); - Err(VpnError::Resolve) - } else { - Ok(message - .answers() - .iter() - .filter_map(|record| record.data().and_then(|data| data.to_ip_addr())) - .collect()) - } + } + + pub fn set_len(&mut self, n: usize) { + unsafe { + self.data.set_len(n); } } } -fn show_info(level: &String) -> bool { - level == "Debug" || level == "Info" || level == "Trace" +impl async_smoltcp::Packet for Packet { + fn as_mut(&mut self) -> &mut [u8] { + self.data.as_mut() + } + + fn as_ref(&self) -> &[u8] { + self.data.as_slice() + } + + fn len(&self) -> usize { + self.data.len() + } } -pub async fn async_run(fd: i32, running: Arc) -> types::Result<()> { - log::error!("vpn process start running"); - let session = Arc::new(platform::Session::new( - fd, - context.options.mtu, - show_info(&context.options.log_level), - )); +impl async_smoltcp::Tun for Session { + type Packet = Packet; - let mut server_ip = Vec::new(); - for _ in 0..10 { - if let Ok(ips) = resolve( - context.options.hostname.as_str(), - (context.options.untrusted_dns.clone() + ":53").as_str(), - ) - .await - { - server_ip = ips; - break; + fn receive(&self) -> std::io::Result> { + let mut file = self.file.deref(); + let mut packet = Packet::new(self.mtu); + match file.read(packet.as_mut()) { + Ok(0) => Err(ErrorKind::BrokenPipe.into()), + Ok(n) => { + packet.set_len(n); + Ok(Some(packet)) + } + Err(err) + if err.kind() == ErrorKind::WouldBlock || err.kind() == ErrorKind::Interrupted => + { + Ok(None) + } + Err(err) => Err(err), } } - log::info!("server ip is {:?}", server_ip); - if server_ip.is_empty() { - return Err(VpnError::Resolve); + fn send(&self, packet: Self::Packet) -> std::io::Result<()> { + let mut file = self.file.deref(); + file.write_all(packet.as_ref()) } - let server_name: ServerName = context.options.hostname.as_str().try_into()?; + fn allocate_packet(&self, len: usize) -> std::io::Result { + Ok(Packet::new(len)) + } +} + +pub async fn init_tls_conn( + connector: TlsConnector, + server_name: ServerName<'static>, +) -> types::Result> { + let looper = LOOPER.read().map_err(|err| Error::Lock(err.to_string()))?; + let domain = looper.config.domain.clone(); + let port = looper.config.port; + let stream = tokio::net::TcpStream::connect((domain, port)).await?; + let conn = connector.connect(server_name, stream).await?; + Ok(conn) +} - let server_addr = SocketAddr::new(server_ip[0], context.options.port); - let dns_addr = dns + ":53"; - let dns_addr: SocketAddr = dns_addr.parse()?; +fn digest_pass(pass: &String) -> String { + let mut encoder = Sha224::new(); + encoder.update(pass.as_bytes()); + let result = encoder.finalize(); + hex::encode(result.as_slice()) +} + +pub async fn async_run(fd: i32, running: Arc) -> types::Result<()> { + log::error!("vpn process start running"); + let config = LOOPER + .read() + .map_err(|err| types::Error::Lock(err.to_string()))? + .config + .clone(); + let session = Arc::new(Session::new(fd)); - let pass = digest_pass(&context.options.password); + let pass = digest_pass(&config.password); let mut root_store = RootCertStore::empty(); root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { @@ -150,11 +160,10 @@ pub async fn async_run(fd: i32, running: Arc) -> types::Result<()> { .with_no_client_auth(), ); - let mut device = TunDevice::new(context.options.mtu, session); - device.add_white_ip(dns_addr.ip()); + let mut device = TunDevice::new(session); - let trusted_addr = (context.options.trusted_dns.clone() + ":53").parse()?; - let distrusted_addr = (context.options.untrusted_dns.clone() + ":53").parse()?; + let trusted_addr = (config.trust_dns.clone() + ":53").parse()?; + let distrusted_addr = (config.distrust_dns.clone() + ":53").parse()?; let empty: SocketAddr = "0.0.0.0:0".parse().unwrap(); let mut header = BytesMut::new(); diff --git a/mobile2/backend/src/types.rs b/mobile2/backend/src/types.rs index ed88383..ff33551 100644 --- a/mobile2/backend/src/types.rs +++ b/mobile2/backend/src/types.rs @@ -6,7 +6,7 @@ use std::{ }; use wry::application::event_loop::{EventLoop, EventLoopBuilder, EventLoopClosed, EventLoopProxy}; -#[derive(Default, Serialize, Deserialize)] +#[derive(Default, Serialize, Deserialize, Clone)] pub struct BnetConfig { pub app: String, pub domain: String, @@ -15,6 +15,7 @@ pub struct BnetConfig { pub gateway: String, pub trust_dns: String, pub distrust_dns: String, + pub mtu: usize, } pub struct MobileTrojanLoop {