diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f61f4684..9ae72286 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,11 +61,11 @@ jobs: run: cargo fmt --all -- --check - name: Check Clippy - run: cargo clippy --all-targets --all-features --all -- -D warnings + run: cargo clippy --all-targets --features full --all -- -D warnings - name: Check features if: ${{ !cancelled() }} - run: cargo hack check --package easytier --each-feature --features aes-gcm --verbose + run: cargo hack check --package easytier --each-feature --features aes-gcm --exclude-features macos-ne --verbose pre-test: name: Build test @@ -87,7 +87,7 @@ jobs: - uses: taiki-e/install-action@nextest - name: Archive test - run: cargo nextest archive --archive-file tests.tar.zst --package easytier --all-features + run: cargo nextest archive --archive-file tests.tar.zst --package easytier --features full - uses: actions/upload-artifact@v4 with: diff --git a/easytier-contrib/easytier-ohrs/Cargo.lock b/easytier-contrib/easytier-ohrs/Cargo.lock index ed7757b8..d8ba3bde 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.lock +++ b/easytier-contrib/easytier-ohrs/Cargo.lock @@ -222,6 +222,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic_refcell" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" + [[package]] name = "auto_impl" version = "1.3.0" @@ -628,6 +634,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -933,6 +948,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_arbitrary" version = "1.4.2" @@ -975,6 +1001,29 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.106", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -1043,6 +1092,7 @@ dependencies = [ "async-stream", "async-trait", "atomic-shim", + "atomic_refcell", "auto_impl", "base64 0.22.1", "bitflags 2.9.4", @@ -1059,7 +1109,9 @@ dependencies = [ "crossbeam", "dashmap", "dbus", + "derivative", "derive_builder", + "derive_more", "easytier-rpc-build", "encoding", "flume", @@ -1101,6 +1153,7 @@ dependencies = [ "prost-reflect-build", "prost-types", "quinn", + "quinn-plaintext", "rand 0.8.5", "rcgen", "regex", @@ -1121,6 +1174,7 @@ dependencies = [ "stun_codec", "sys-locale", "tabled", + "terminal_size", "thiserror 1.0.69", "thunk-rs", "time", @@ -1135,6 +1189,7 @@ dependencies = [ "tracing", "tracing-subscriber", "tun-easytier", + "unicode-width 0.1.11", "url", "uuid", "version-compare", @@ -2572,7 +2627,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b27250baa967a15214e57384dd6228c59afbccb15ab8f97207c9758917544bf5" dependencies = [ - "convert_case", + "convert_case 0.8.0", "proc-macro2", "quote", "semver", @@ -2585,7 +2640,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c844efa85d53b5adc3b326520f3a108c3a737b7534ee10d406f81884e7e71b3c" dependencies = [ - "convert_case", + "convert_case 0.8.0", "ctor", "napi-derive-backend-ohos", "proc-macro2", @@ -3369,13 +3424,25 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.0", "thiserror 2.0.16", "tokio", "tracing", "web-time", ] +[[package]] +name = "quinn-plaintext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e617feaeb6493018fa35fc47ae8b630ac8903d8159e9e747018841b99bad3d" +dependencies = [ + "bytes", + "quinn-proto", + "seahash", + "tracing", +] + [[package]] name = "quinn-proto" version = "0.11.13" @@ -3408,9 +3475,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -3795,7 +3862,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -3857,6 +3924,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.11.1" @@ -4775,6 +4848,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index b712b9c0..a98a80f0 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -394,3 +394,5 @@ tracing = ["tokio/tracing", "dep:console-subscriber"] magic-dns = ["dep:hickory-client", "dep:hickory-server"] faketcp = ["dep:flume"] zstd = ["dep:zstd"] +# For Network Extension on macOS +macos-ne = [] diff --git a/easytier/src/common/ifcfg/mod.rs b/easytier/src/common/ifcfg/mod.rs index f5bd223a..568e71bc 100644 --- a/easytier/src/common/ifcfg/mod.rs +++ b/easytier/src/common/ifcfg/mod.rs @@ -1,4 +1,7 @@ -#[cfg(any(target_os = "macos", target_os = "freebsd"))] +#[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" +))] mod darwin; #[cfg(target_os = "linux")] mod netlink; @@ -144,14 +147,17 @@ impl IfConfiguerTrait for DummyIfConfiger {} #[cfg(target_os = "linux")] pub type IfConfiger = netlink::NetlinkIfConfiger; -#[cfg(any(target_os = "macos", target_os = "freebsd"))] +#[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" +))] pub type IfConfiger = darwin::MacIfConfiger; #[cfg(target_os = "windows")] pub type IfConfiger = windows::WindowsIfConfiger; #[cfg(not(any( - target_os = "macos", + all(target_os = "macos", not(feature = "macos-ne")), target_os = "linux", target_os = "windows", target_os = "freebsd", diff --git a/easytier/src/common/mod.rs b/easytier/src/common/mod.rs index 5b54c436..8f7fbd2e 100644 --- a/easytier/src/common/mod.rs +++ b/easytier/src/common/mod.rs @@ -120,7 +120,7 @@ pub fn get_machine_id() -> uuid::Uuid { #[cfg(any( target_os = "linux", - target_os = "macos", + all(target_os = "macos", not(feature = "macos-ne")), target_os = "windows", target_os = "freebsd" ))] @@ -137,7 +137,7 @@ pub fn get_machine_id() -> uuid::Uuid { #[cfg(not(any( target_os = "linux", - target_os = "macos", + all(target_os = "macos", not(feature = "macos-ne")), target_os = "windows", target_os = "freebsd" )))] diff --git a/easytier/src/common/network.rs b/easytier/src/common/network.rs index df48ab46..78bd4a5a 100644 --- a/easytier/src/common/network.rs +++ b/easytier/src/common/network.rs @@ -16,7 +16,11 @@ struct InterfaceFilter { iface: NetworkInterface, } -#[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] +#[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" +))] impl InterfaceFilter { async fn filter_iface(&self) -> bool { true @@ -60,13 +64,16 @@ impl InterfaceFilter { } // Cache for networksetup command output -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "macos-ne")))] static NETWORKSETUP_CACHE: std::sync::OnceLock> = std::sync::OnceLock::new(); -#[cfg(any(target_os = "macos", target_os = "freebsd"))] +#[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" +))] impl InterfaceFilter { - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] async fn get_networksetup_output() -> String { use anyhow::Context; use std::time::{Duration, Instant}; @@ -101,7 +108,7 @@ impl InterfaceFilter { stdout } - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] async fn is_interface_physical(&self) -> bool { let interface_name = &self.iface.name; let stdout = Self::get_networksetup_output().await; diff --git a/easytier/src/connector/mod.rs b/easytier/src/connector/mod.rs index e85d0099..8f8d08da 100644 --- a/easytier/src/connector/mod.rs +++ b/easytier/src/connector/mod.rs @@ -36,7 +36,7 @@ async fn set_bind_addr_for_peer_connector( ) { if cfg!(any( target_os = "android", - target_os = "ios", + any(target_os = "ios", feature = "macos-ne"), target_env = "ohos" )) { return; diff --git a/easytier/src/gateway/tcp_proxy.rs b/easytier/src/gateway/tcp_proxy.rs index aed77620..72c480ba 100644 --- a/easytier/src/gateway/tcp_proxy.rs +++ b/easytier/src/gateway/tcp_proxy.rs @@ -539,7 +539,7 @@ impl TcpProxy { || self.global_ctx.no_tun() || cfg!(any( target_os = "android", - target_os = "ios", + any(target_os = "ios", feature = "macos-ne"), target_env = "ohos" )) { diff --git a/easytier/src/instance/dns_server/server_instance.rs b/easytier/src/instance/dns_server/server_instance.rs index eab04695..21288295 100644 --- a/easytier/src/instance/dns_server/server_instance.rs +++ b/easytier/src/instance/dns_server/server_instance.rs @@ -464,7 +464,7 @@ fn get_system_config( return Ok(Some(Box::new(WindowsDNSManager::new(tun_name)?))); } - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] { use super::system_config::darwin::DarwinConfigurator; return Ok(Some(Box::new(DarwinConfigurator::new()))); diff --git a/easytier/src/instance/dns_server/system_config/mod.rs b/easytier/src/instance/dns_server/system_config/mod.rs index c4a735a0..388ea3e8 100644 --- a/easytier/src/instance/dns_server/system_config/mod.rs +++ b/easytier/src/instance/dns_server/system_config/mod.rs @@ -4,7 +4,7 @@ pub mod linux; #[cfg(target_os = "windows")] pub mod windows; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "macos-ne")))] pub mod darwin; #[derive(Default, Debug)] diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 9bcfbbc5..5506cdf0 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -114,7 +114,7 @@ impl IpProxy { tracing::error!("start icmp proxy failed: {:?}", e); if cfg!(not(any( target_os = "android", - target_os = "ios", + any(target_os = "ios", feature = "macos-ne"), target_env = "ohos" ))) { // android, ios and ohos not support icmp proxy @@ -802,7 +802,11 @@ impl Instance { } #[cfg(all( - not(any(target_os = "android", target_os = "ios", target_env = "ohos")), + not(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + )), feature = "tun" ))] { @@ -846,7 +850,11 @@ impl Instance { } #[cfg(all( - not(any(target_os = "android", target_os = "ios", target_env = "ohos")), + not(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + )), feature = "tun" ))] fn check_for_static_ip(&self, first_round_output: oneshot::Sender>) { @@ -936,7 +944,11 @@ impl Instance { { Self::clear_nic_ctx(self.nic_ctx.clone(), self.peer_packet_receiver.clone()).await; - #[cfg(not(any(target_os = "android", target_os = "ios", target_env = "ohos")))] + #[cfg(not(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + )))] if !self.global_ctx.config.get_flags().no_tun { let (output_tx, output_rx) = oneshot::channel(); self.check_for_static_ip(output_tx); @@ -1440,7 +1452,11 @@ impl Instance { self.peer_packet_receiver.clone() } - #[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] + #[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + ))] pub async fn setup_nic_ctx_for_mobile( nic_ctx: ArcNicCtx, global_ctx: ArcGlobalCtx, diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index c96c9ee1..6a67d65e 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -523,7 +523,7 @@ impl VirtualNic { } } - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] config.platform_config(|config| { // disable packet information so we can process the header by ourselves, see tun2 impl for more details config.packet_information(false); @@ -583,7 +583,11 @@ impl VirtualNic { Ok(tun::create(&config)?) } - #[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] + #[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + ))] pub async fn create_dev_for_mobile( &mut self, tun_fd: std::os::fd::RawFd, @@ -592,7 +596,7 @@ impl VirtualNic { let mut config = Configuration::default(); config.layer(Layer::L3); - #[cfg(target_os = "ios")] + #[cfg(any(target_os = "ios", feature = "macos-ne"))] config.platform_config(|config| { // disable packet information so we can process the header by ourselves, see tun2 impl for more details config.packet_information(false); @@ -602,7 +606,7 @@ impl VirtualNic { config.close_fd_on_drop(false); config.up(); - let has_packet_info = cfg!(target_os = "ios"); + let has_packet_info = cfg!(any(target_os = "ios", feature = "macos-ne")); let dev = tun::create(&config)?; let dev = AsyncDevice::new(dev)?; let (a, b) = BiLock::new(dev); @@ -680,7 +684,7 @@ impl VirtualNic { self.ifcfg.set_mtu(ifname.as_str(), mtu_in_config).await?; } - let has_packet_info = cfg!(target_os = "macos"); + let has_packet_info = cfg!(all(target_os = "macos", not(feature = "macos-ne"))); let (a, b) = BiLock::new(dev); let ft = TunnelWrapper::new( TunStream::new(a, has_packet_info), @@ -827,7 +831,10 @@ impl NicCtx { nic.remove_ip(None).await?; nic.add_ip(ipv4_addr.address(), ipv4_addr.network_length() as i32) .await?; - #[cfg(any(target_os = "macos", target_os = "freebsd"))] + #[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" + ))] { nic.add_route(ipv4_addr.first_address(), ipv4_addr.network_length()) .await?; @@ -841,7 +848,10 @@ impl NicCtx { nic.remove_ipv6(None).await?; nic.add_ipv6(ipv6_addr.address(), ipv6_addr.network_length() as i32) .await?; - #[cfg(any(target_os = "macos", target_os = "freebsd"))] + #[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" + ))] { nic.add_ipv6_route(ipv6_addr.first_address(), ipv6_addr.network_length()) .await?; @@ -1134,7 +1144,10 @@ impl NicCtx { let _ = RegistryManager::reg_change_catrgory_in_profile(&dev_name); } - #[cfg(any(target_os = "macos", target_os = "freebsd"))] + #[cfg(any( + all(target_os = "macos", not(feature = "macos-ne")), + target_os = "freebsd" + ))] { // remove the 10.0.0.0/24 route (which is added by rust-tun by default) let _ = nic @@ -1175,7 +1188,11 @@ impl NicCtx { Ok(()) } - #[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] + #[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + ))] pub async fn run_for_mobile(&mut self, tun_fd: std::os::fd::RawFd) -> Result<(), Error> { let tunnel = { let mut nic = self.nic.lock().await; diff --git a/easytier/src/launcher.rs b/easytier/src/launcher.rs index 3834a189..222c4ffa 100644 --- a/easytier/src/launcher.rs +++ b/easytier/src/launcher.rs @@ -93,7 +93,11 @@ impl EasyTierLauncher { } } - #[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] + #[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + ))] async fn run_routine_for_mobile( instance: &Instance, data: &EasyTierData, @@ -152,7 +156,11 @@ impl EasyTierLauncher { } }); - #[cfg(any(target_os = "android", target_os = "ios", target_env = "ohos"))] + #[cfg(any( + target_os = "android", + any(target_os = "ios", feature = "macos-ne"), + target_env = "ohos" + ))] Self::run_routine_for_mobile(&instance, &data, &mut tasks).await; instance.run().await?; diff --git a/easytier/src/service_manager/mod.rs b/easytier/src/service_manager/mod.rs index 785f2c06..83dc5f26 100644 --- a/easytier/src/service_manager/mod.rs +++ b/easytier/src/service_manager/mod.rs @@ -49,7 +49,7 @@ impl Service { pub fn new(name: String) -> Result { #[cfg(target_os = "windows")] let service_manager = Box::new(self::win_service_manager::WinServiceManager::new()?); - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] let service_manager: Box = Box::new(service_manager::TypedServiceManager::Launchd( service_manager::LaunchdServiceManager::system().with_config( @@ -63,7 +63,10 @@ impl Service { ), )); - #[cfg(not(any(target_os = "windows", target_os = "macos")))] + #[cfg(not(any( + target_os = "windows", + all(target_os = "macos", not(feature = "macos-ne")) + )))] let service_manager: Box = Box::new(service_manager::TypedServiceManager::native()?); diff --git a/easytier/src/tunnel/fake_tcp/netfilter/mod.rs b/easytier/src/tunnel/fake_tcp/netfilter/mod.rs index 960920d8..589547f0 100644 --- a/easytier/src/tunnel/fake_tcp/netfilter/mod.rs +++ b/easytier/src/tunnel/fake_tcp/netfilter/mod.rs @@ -26,7 +26,7 @@ cfg_if::cfg_if! { } } } - } else if #[cfg(target_os = "macos")] { + } else if #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] { pub mod macos_bpf; pub fn create_tun(