diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ff4da26..f5d0cbf2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -95,6 +95,9 @@ jobs: bash ./.github/workflows/install_rust.sh rustup component add rustfmt rustup component add clippy + + - name: Install cargo-hack + run: cargo install cargo-hack --locked - name: Check formatting if: ${{ !cancelled() }} @@ -107,6 +110,10 @@ jobs: mkdir -p easytier-gui/dist cargo clippy --all-targets --all-features --all -- -D warnings + - name: Check features + if: ${{ !cancelled() }} + run: cargo hack check --package easytier --each-feature --features aes-gcm --verbose + - name: Run tests run: | sudo prlimit --pid $$ --nofile=1048576:1048576 diff --git a/easytier-contrib/easytier-ohrs/Cargo.lock b/easytier-contrib/easytier-ohrs/Cargo.lock index 08e9e163..ed7757b8 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.lock +++ b/easytier-contrib/easytier-ohrs/Cargo.lock @@ -38,6 +38,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -133,6 +147,12 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-recursion" version = "1.1.1" @@ -254,14 +274,14 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.9.4", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.11.0", "proc-macro2", "quote", "regex", @@ -540,6 +560,16 @@ dependencies = [ "clap", ] +[[package]] +name = "clap_complete_nushell" +version = "4.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685bc86fd34b7467e0532a4f8435ab107960d69a243785ef0275e571b35b641a" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.5.47" @@ -746,6 +776,15 @@ version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -995,7 +1034,7 @@ checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" [[package]] name = "easytier" -version = "2.4.5" +version = "2.5.0" dependencies = [ "anyhow", "arc-swap", @@ -1011,16 +1050,19 @@ dependencies = [ "bytecodec", "byteorder", "bytes", + "cfg-if", "chrono", "cidr", "clap", "clap_complete", + "clap_complete_nushell", "crossbeam", "dashmap", "dbus", "derive_builder", "easytier-rpc-build", "encoding", + "flume", "futures", "gethostname", "git-version", @@ -1046,6 +1088,7 @@ dependencies = [ "network-interface", "nix 0.29.0", "once_cell", + "ordered_hash_map", "parking_lot", "percent-encoding", "petgraph 0.8.2", @@ -1073,6 +1116,7 @@ dependencies = [ "sha2", "shellexpand", "smoltcp", + "snow", "socket2 0.5.10", "stun_codec", "sys-locale", @@ -1097,10 +1141,12 @@ dependencies = [ "which 7.0.3", "wildmatch", "winapi", + "windivert", "windows 0.52.0", "windows-service", "windows-sys 0.52.0", "winreg 0.52.0", + "x25519-dalek", "zerocopy 0.7.35", "zip", "zstd", @@ -1126,8 +1172,6 @@ dependencies = [ [[package]] name = "easytier-rpc-build" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24829168c28f6a448f57d18116c255dcbd2b8c25e76dbc60f6cd16d68ad2cf07" dependencies = [ "heck 0.5.0", "prost-build", @@ -1264,10 +1308,19 @@ dependencies = [ ] [[package]] -name = "fastbloom" -version = "0.14.0" +name = "etherparse" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c1ddb9231d8554c2d6bdf4cfaabf0c59251658c68b6c95cd52dd0c513a912a" +checksum = "827292ea592108849932ad8e30218f8b1f21c0dfd0696698a18b5d0aed62d990" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "fastbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" dependencies = [ "getrandom 0.3.3", "libm", @@ -1280,6 +1333,9 @@ name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +dependencies = [ + "getrandom 0.2.16", +] [[package]] name = "fiat-crypto" @@ -1310,6 +1366,18 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "fastrand", + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1496,6 +1564,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1605,9 +1683,9 @@ checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heapless" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" dependencies = [ "hash32", "stable_deref_trait", @@ -2161,15 +2239,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -2230,7 +2299,7 @@ dependencies = [ [[package]] name = "kcp-sys" version = "0.1.0" -source = "git+https://github.com/EasyTier/kcp-sys?rev=71eff18c573a4a71bf99c7fabc6a8b9f211c84c1#71eff18c573a4a71bf99c7fabc6a8b9f211c84c1" +source = "git+https://github.com/EasyTier/kcp-sys?rev=94964794caaed5d388463137da59b97499619e5f#94964794caaed5d388463137da59b97499619e5f" dependencies = [ "anyhow", "auto_impl", @@ -2564,7 +2633,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", "security-framework 2.11.1", @@ -2810,6 +2879,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-sys" version = "0.9.109" @@ -2822,6 +2897,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered_hash_map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c699f8a30f345785be969deed7eee4c73a5de58c7faf61d6a3251ef798ff61" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "papergrid" version = "0.12.0" @@ -2874,12 +2958,12 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] @@ -3045,6 +3129,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -3273,7 +3369,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.0", + "socket2 0.5.10", "thiserror 2.0.16", "tokio", "tracing", @@ -3312,9 +3408,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -3652,14 +3748,14 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.1", "rustls-pki-types", "schannel", - "security-framework 3.5.0", + "security-framework 3.5.1", ] [[package]] @@ -3683,9 +3779,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", @@ -3696,10 +3792,10 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.5.0", + "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3776,9 +3872,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags 2.9.4", "core-foundation 0.10.1", @@ -3987,6 +4083,23 @@ dependencies = [ "managed", ] +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "getrandom 0.3.3", + "ring", + "rustc_version", + "sha2", + "subtle", +] + [[package]] name = "socket2" version = "0.5.10" @@ -4007,6 +4120,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +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" version = "1.2.0" @@ -4895,9 +5017,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" dependencies = [ "rustls-pki-types", ] @@ -4987,6 +5109,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windivert" +version = "0.6.0" +source = "git+https://github.com/EasyTier/windivert-rust.git?rev=adcc56d1550f7b5377ec2b3429f413ee24a77375#adcc56d1550f7b5377ec2b3429f413ee24a77375" +dependencies = [ + "etherparse", + "thiserror 1.0.69", + "windivert-sys", + "windows 0.48.0", +] + +[[package]] +name = "windivert-sys" +version = "0.10.0" +source = "git+https://github.com/EasyTier/windivert-rust.git?rev=adcc56d1550f7b5377ec2b3429f413ee24a77375#adcc56d1550f7b5377ec2b3429f413ee24a77375" +dependencies = [ + "cc", + "thiserror 1.0.69", + "windows 0.48.0", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows" version = "0.52.0" diff --git a/easytier-web/src/main.rs b/easytier-web/src/main.rs index 25ac0b9f..3cb1374d 100644 --- a/easytier-web/src/main.rs +++ b/easytier-web/src/main.rs @@ -6,6 +6,7 @@ extern crate rust_i18n; use std::sync::Arc; use clap::Parser; +use easytier::tunnel::websocket::WSTunnelListener; use easytier::{ common::{ config::{ConsoleLoggerConfig, FileLoggerConfig, LoggingConfigLoader}, @@ -13,9 +14,7 @@ use easytier::{ error::Error, network::{local_ipv4, local_ipv6}, }, - tunnel::{ - tcp::TcpTunnelListener, udp::UdpTunnelListener, websocket::WSTunnelListener, TunnelListener, - }, + tunnel::{tcp::TcpTunnelListener, udp::UdpTunnelListener, TunnelListener}, utils::{init_logger, setup_panic_handler}, }; diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index e271aefb..7daf1602 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -155,7 +155,7 @@ bitflags = "2.5" aes-gcm = { version = "0.10.3", optional = true } openssl = { version = "0.10", optional = true, features = ["vendored"] } snow = "0.10.0" -x25519-dalek = "2.0" +x25519-dalek = { version = "2.0", features = ["static_secrets"] } # for cli tabled = "0.16" @@ -185,7 +185,7 @@ smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "0a926767a6 # "socket-tcp-cubic", "async", ] } -parking_lot = { version = "0.12.0", optional = true } +parking_lot = { version = "0.12.0" } wildmatch = "2.3.4" @@ -199,7 +199,7 @@ service-manager = { git = "https://github.com/EasyTier/service-manager-rs.git", zstd = { version = "0.13" } -kcp-sys = { git = "https://github.com/EasyTier/kcp-sys", rev = "94964794caaed5d388463137da59b97499619e5f" } +kcp-sys = { git = "https://github.com/EasyTier/kcp-sys", rev = "94964794caaed5d388463137da59b97499619e5f", optional = true } prost-reflect = { version = "0.14.5", default-features = false, features = [ "derive", @@ -215,8 +215,9 @@ hickory-resolver = "0.25.2" hickory-proto = "0.25.2" # for magic dns -hickory-client = "0.25.2" -hickory-server = { version = "0.25.2", features = ["resolver"] } +hickory-client = { version = "0.25.2", optional = true } +hickory-server = { version = "0.25.2", features = ["resolver"], optional = true } + derive_builder = "0.20.2" humantime-serde = "1.1.1" multimap = "0.10.1" @@ -318,7 +319,7 @@ tokio-socks = "0.5.2" [features] -default = ["wireguard", "websocket", "smoltcp", "tun", "socks5", "quic"] +default = ["wireguard", "websocket", "smoltcp", "tun", "socks5", "kcp", "quic", "magic-dns"] full = [ "websocket", "wireguard", @@ -327,9 +328,11 @@ full = [ "smoltcp", "tun", "socks5", + "magic-dns", ] wireguard = ["dep:boringtun", "dep:ring"] quic = ["dep:quinn", "dep:rustls", "dep:rcgen"] +kcp = ["dep:kcp-sys"] mimalloc = ["dep:mimalloc"] aes-gcm = ["dep:aes-gcm"] openssl-crypto = ["dep:openssl"] @@ -341,8 +344,9 @@ websocket = [ "dep:rustls", "dep:rcgen", ] -smoltcp = ["dep:smoltcp", "dep:parking_lot"] -socks5 = ["dep:smoltcp"] +smoltcp = ["dep:smoltcp"] +socks5 = ["smoltcp"] jemalloc = ["dep:jemallocator", "dep:jemalloc-sys"] jemalloc-prof = ["jemalloc", "dep:jemalloc-ctl", "jemalloc-ctl/stats", "jemalloc-sys/profiling", "jemalloc-sys/stats"] tracing = ["tokio/tracing", "dep:console-subscriber"] +magic-dns = ["dep:hickory-client", "dep:hickory-server"] diff --git a/easytier/src/core.rs b/easytier/src/core.rs index b91ed29f..55dce023 100644 --- a/easytier/src/core.rs +++ b/easytier/src/core.rs @@ -931,7 +931,6 @@ impl NetworkOptions { )); } - #[cfg(feature = "socks5")] for port_forward in self.port_forward.iter() { let example_str = ", example: udp://0.0.0.0:12345/10.126.126.1:12345"; diff --git a/easytier/src/gateway/kcp_proxy.rs b/easytier/src/gateway/kcp_proxy.rs index a5c8bfe9..18b3e443 100644 --- a/easytier/src/gateway/kcp_proxy.rs +++ b/easytier/src/gateway/kcp_proxy.rs @@ -13,19 +13,9 @@ use kcp_sys::{ packet_def::KcpPacket, stream::KcpStream, }; -use pnet::packet::{ - ip::IpNextHeaderProtocols, - ipv4::Ipv4Packet, - tcp::{TcpFlags, TcpPacket}, - Packet as _, -}; +use pnet::packet::ipv4::Ipv4Packet; use prost::Message; -use tokio::{ - io::{copy_bidirectional, AsyncRead, AsyncWrite}, - select, - task::JoinSet, -}; -use tokio_util::io::InspectReader; +use tokio::{select, task::JoinSet}; use super::{ tcp_proxy::{NatDstConnector, NatDstTcpConnector, TcpProxy}, @@ -37,9 +27,10 @@ use crate::{ error::Result, global_ctx::{ArcGlobalCtx, GlobalCtx}, }, - peers::{acl_filter::AclFilter, peer_manager::PeerManager, NicPacketFilter, PeerPacketFilter}, + gateway::wrapped_proxy::{ProxyAclHandler, TcpProxyForWrappedSrcTrait}, + peers::{peer_manager::PeerManager, PeerPacketFilter}, proto::{ - acl::{Action, ChainType, Protocol}, + acl::{ChainType, Protocol}, api::instance::{ ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, @@ -215,21 +206,14 @@ impl NatDstConnector for NatDstKcpConnector { struct TcpProxyForKcpSrc(Arc>); #[async_trait::async_trait] -pub(crate) trait TcpProxyForKcpSrcTrait: Send + Sync + 'static { - type Connector: NatDstConnector; - fn get_tcp_proxy(&self) -> &Arc>; - async fn check_dst_allow_kcp_input(&self, dst_ip: &Ipv4Addr) -> bool; -} - -#[async_trait::async_trait] -impl TcpProxyForKcpSrcTrait for TcpProxyForKcpSrc { +impl TcpProxyForWrappedSrcTrait for TcpProxyForKcpSrc { type Connector = NatDstKcpConnector; fn get_tcp_proxy(&self) -> &Arc> { &self.0 } - async fn check_dst_allow_kcp_input(&self, dst_ip: &Ipv4Addr) -> bool { + async fn check_dst_allow_wrapped_input(&self, dst_ip: &Ipv4Addr) -> bool { let Some(peer_manager) = self.0.get_peer_manager() else { return false; }; @@ -239,81 +223,6 @@ impl TcpProxyForKcpSrcTrait for TcpProxyForKcpSrc { } } -#[async_trait::async_trait] -impl> NicPacketFilter for T { - async fn try_process_packet_from_nic(&self, zc_packet: &mut ZCPacket) -> bool { - let ret = self - .get_tcp_proxy() - .try_process_packet_from_nic(zc_packet) - .await; - if ret { - return true; - } - - let data = zc_packet.payload(); - let ip_packet = Ipv4Packet::new(data).unwrap(); - if ip_packet.get_version() != 4 - || ip_packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp - { - return false; - } - - // if no connection is established, only allow SYN packet - let tcp_packet = TcpPacket::new(ip_packet.payload()).unwrap(); - let is_syn = tcp_packet.get_flags() & TcpFlags::SYN != 0 - && tcp_packet.get_flags() & TcpFlags::ACK == 0; - if is_syn { - // only check dst feature flag when SYN packet - if !self - .check_dst_allow_kcp_input(&ip_packet.get_destination()) - .await - { - tracing::warn!( - "{:?} proxy src: dst {} not allow kcp input", - self.get_tcp_proxy().get_transport_type(), - ip_packet.get_destination() - ); - return false; - } - } else { - // if not syn packet, only allow established connection - if !self - .get_tcp_proxy() - .is_tcp_proxy_connection(SocketAddr::new( - IpAddr::V4(ip_packet.get_source()), - tcp_packet.get_source(), - )) - { - return false; - } - } - - if let Some(my_ipv4) = self.get_tcp_proxy().get_global_ctx().get_ipv4() { - // this is a net-to-net packet, only allow it when smoltcp is enabled - // because the syn-ack packet will not be through and handled by the tun device when - // the source ip is in the local network - if ip_packet.get_source() != my_ipv4.address() - && !self.get_tcp_proxy().is_smoltcp_enabled() - { - tracing::warn!( - "{:?} nat 2 nat packet, src: {} dst: {} not allow kcp input", - self.get_tcp_proxy().get_transport_type(), - ip_packet.get_source(), - ip_packet.get_destination() - ); - return false; - } - }; - - let hdr = zc_packet.mut_peer_manager_header().unwrap(); - hdr.to_peer_id = self.get_tcp_proxy().get_my_peer_id().into(); - if self.get_tcp_proxy().get_transport_type() == TcpProxyEntryTransportType::Kcp { - hdr.set_kcp_src_modified(true); - } - true - } -} - pub struct KcpProxySrc { kcp_endpoint: Arc, peer_manager: Arc, @@ -387,50 +296,6 @@ pub struct KcpProxyDst { tasks: JoinSet<()>, } -#[derive(Clone)] -pub struct ProxyAclHandler { - pub acl_filter: Arc, - pub packet_info: PacketInfo, - pub chain_type: ChainType, -} - -impl ProxyAclHandler { - pub fn handle_packet(&self, buf: &[u8]) -> Result<()> { - let mut packet_info = self.packet_info.clone(); - packet_info.packet_size = buf.len(); - let ret = self - .acl_filter - .get_processor() - .process_packet(&packet_info, self.chain_type); - self.acl_filter.handle_acl_result( - &ret, - &packet_info, - self.chain_type, - &self.acl_filter.get_processor(), - ); - if !matches!(ret.action, Action::Allow) { - return Err(anyhow::anyhow!("acl denied").into()); - } - - Ok(()) - } - - pub async fn copy_bidirection_with_acl( - &self, - src: impl AsyncRead + AsyncWrite + Unpin, - mut dst: impl AsyncRead + AsyncWrite + Unpin, - ) -> Result<()> { - let (src_reader, src_writer) = tokio::io::split(src); - let src_reader = InspectReader::new(src_reader, |buf| { - let _ = self.handle_packet(buf); - }); - let mut src = tokio::io::join(src_reader, src_writer); - - copy_bidirectional(&mut src, &mut dst).await?; - Ok(()) - } -} - impl KcpProxyDst { pub async fn new(peer_manager: Arc) -> Self { let mut kcp_endpoint = create_kcp_endpoint(); diff --git a/easytier/src/gateway/mod.rs b/easytier/src/gateway/mod.rs index 596dee69..ab057635 100644 --- a/easytier/src/gateway/mod.rs +++ b/easytier/src/gateway/mod.rs @@ -16,8 +16,11 @@ pub mod fast_socks5; #[cfg(feature = "socks5")] pub mod socks5; +#[cfg(feature = "kcp")] pub mod kcp_proxy; +mod wrapped_proxy; +#[cfg(feature = "quic")] pub mod quic_proxy; #[derive(Debug)] diff --git a/easytier/src/gateway/quic_proxy.rs b/easytier/src/gateway/quic_proxy.rs index bc7cbeae..471d3837 100644 --- a/easytier/src/gateway/quic_proxy.rs +++ b/easytier/src/gateway/quic_proxy.rs @@ -17,8 +17,8 @@ use crate::common::error::Result; use crate::common::global_ctx::{ArcGlobalCtx, GlobalCtx}; use crate::common::join_joinset_background; use crate::defer; -use crate::gateway::kcp_proxy::{ProxyAclHandler, TcpProxyForKcpSrcTrait}; use crate::gateway::tcp_proxy::{NatDstConnector, NatDstTcpConnector, TcpProxy}; +use crate::gateway::wrapped_proxy::{ProxyAclHandler, TcpProxyForWrappedSrcTrait}; use crate::gateway::CidrSet; use crate::peers::peer_manager::PeerManager; use crate::proto::acl::{ChainType, Protocol}; @@ -184,14 +184,14 @@ impl NatDstConnector for NatDstQUICConnector { struct TcpProxyForQUICSrc(Arc>); #[async_trait::async_trait] -impl TcpProxyForKcpSrcTrait for TcpProxyForQUICSrc { +impl TcpProxyForWrappedSrcTrait for TcpProxyForQUICSrc { type Connector = NatDstQUICConnector; fn get_tcp_proxy(&self) -> &Arc> { &self.0 } - async fn check_dst_allow_kcp_input(&self, dst_ip: &Ipv4Addr) -> bool { + async fn check_dst_allow_wrapped_input(&self, dst_ip: &Ipv4Addr) -> bool { let Some(peer_manager) = self.0.get_peer_manager() else { return false; }; diff --git a/easytier/src/gateway/socks5.rs b/easytier/src/gateway/socks5.rs index 2b1a48dc..7fe62d24 100644 --- a/easytier/src/gateway/socks5.rs +++ b/easytier/src/gateway/socks5.rs @@ -9,9 +9,12 @@ use std::{ }; use crossbeam::atomic::AtomicCell; +#[cfg(feature = "kcp")] use kcp_sys::{endpoint::KcpEndpoint, stream::KcpStream}; use tokio_util::sync::{CancellationToken, DropGuard}; +#[cfg(feature = "kcp")] +use crate::gateway::kcp_proxy::NatDstKcpConnector; use crate::{ common::{ config::PortForwardConfig, global_ctx::GlobalCtxEvent, join_joinset_background, @@ -25,7 +28,6 @@ use crate::{ util::stream::tcp_connect_with_timeout, }, ip_reassembler::IpReassembler, - kcp_proxy::NatDstKcpConnector, tokio_smoltcp::{channel_device, BufferSize, Net, NetConfig}, }, tunnel::{ @@ -52,6 +54,7 @@ use crate::{ peers::{peer_manager::PeerManager, PeerPacketFilter}, }; +#[cfg(feature = "kcp")] use super::tcp_proxy::NatDstConnector as _; enum SocksUdpSocket { @@ -78,6 +81,7 @@ impl SocksUdpSocket { enum SocksTcpStream { Tcp(tokio::net::TcpStream), SmolTcp(super::tokio_smoltcp::TcpStream), + #[cfg(feature = "kcp")] Kcp(KcpStream), } @@ -92,6 +96,7 @@ impl AsyncRead for SocksTcpStream { SocksTcpStream::SmolTcp(ref mut stream) => { std::pin::Pin::new(stream).poll_read(cx, buf) } + #[cfg(feature = "kcp")] SocksTcpStream::Kcp(ref mut stream) => std::pin::Pin::new(stream).poll_read(cx, buf), } } @@ -108,6 +113,7 @@ impl AsyncWrite for SocksTcpStream { SocksTcpStream::SmolTcp(ref mut stream) => { std::pin::Pin::new(stream).poll_write(cx, buf) } + #[cfg(feature = "kcp")] SocksTcpStream::Kcp(ref mut stream) => std::pin::Pin::new(stream).poll_write(cx, buf), } } @@ -119,6 +125,7 @@ impl AsyncWrite for SocksTcpStream { match self.get_mut() { SocksTcpStream::Tcp(ref mut stream) => std::pin::Pin::new(stream).poll_flush(cx), SocksTcpStream::SmolTcp(ref mut stream) => std::pin::Pin::new(stream).poll_flush(cx), + #[cfg(feature = "kcp")] SocksTcpStream::Kcp(ref mut stream) => std::pin::Pin::new(stream).poll_flush(cx), } } @@ -130,6 +137,7 @@ impl AsyncWrite for SocksTcpStream { match self.get_mut() { SocksTcpStream::Tcp(ref mut stream) => std::pin::Pin::new(stream).poll_shutdown(cx), SocksTcpStream::SmolTcp(ref mut stream) => std::pin::Pin::new(stream).poll_shutdown(cx), + #[cfg(feature = "kcp")] SocksTcpStream::Kcp(ref mut stream) => std::pin::Pin::new(stream).poll_shutdown(cx), } } @@ -211,12 +219,14 @@ impl Drop for SmolTcpConnector { } } +#[cfg(feature = "kcp")] struct Socks5KcpConnector { kcp_endpoint: Weak, peer_mgr: Weak, src_addr: SocketAddr, } +#[cfg(feature = "kcp")] #[async_trait::async_trait] impl AsyncTcpConnector for Socks5KcpConnector { type S = SocksTcpStream; @@ -242,6 +252,7 @@ impl AsyncTcpConnector for Socks5KcpConnector { } struct Socks5AutoConnector { + #[cfg(feature = "kcp")] kcp_endpoint: Option>, peer_mgr: Weak, entries: Socks5EntrySet, @@ -288,6 +299,7 @@ impl AsyncTcpConnector for Socks5AutoConnector { let dst_allow_kcp = peer_mgr_arc.check_allow_kcp_to_dst(&addr.ip()).await; tracing::debug!("dst_allow_kcp: {:?}", dst_allow_kcp); + #[cfg(feature = "kcp")] let connector: Box + Send> = match (&self.kcp_endpoint, dst_allow_kcp) { (Some(kcp_endpoint), true) => Box::new(Socks5KcpConnector { @@ -301,6 +313,12 @@ impl AsyncTcpConnector for Socks5AutoConnector { current_entry: std::sync::Mutex::new(None), }), }; + #[cfg(not(feature = "kcp"))] + let connector = Box::new(SmolTcpConnector { + net: self.smoltcp_net.clone().unwrap(), + entries: self.entries.clone(), + current_entry: std::sync::Mutex::new(None), + }); let ret = connector.tcp_connect(addr, timeout_s).await; self.inner_connector.lock().replace(Box::new(connector)); @@ -490,6 +508,7 @@ pub struct Socks5Server { udp_client_map: Arc>>, udp_forward_task: Arc>>, + #[cfg(feature = "kcp")] kcp_endpoint: Mutex>>, socks5_enabled: Arc, @@ -603,6 +622,7 @@ impl Socks5Server { udp_client_map: Arc::new(DashMap::new()), udp_forward_task: Arc::new(DashMap::new()), + #[cfg(feature = "kcp")] kcp_endpoint: Mutex::new(None), socks5_enabled: Arc::new(AtomicBool::new(false)), @@ -662,9 +682,12 @@ impl Socks5Server { pub async fn run( self: &Arc, - kcp_endpoint: Option>, + #[cfg(feature = "kcp")] kcp_endpoint: Option>, ) -> Result<(), Error> { - *self.kcp_endpoint.lock().await = kcp_endpoint.clone(); + #[cfg(feature = "kcp")] + { + *self.kcp_endpoint.lock().await = kcp_endpoint.clone(); + } if let Some(proxy_url) = self.global_ctx.config.get_socks5_portal() { let bind_addr = format!( "{}:{}", @@ -692,6 +715,7 @@ impl Socks5Server { .as_ref() .map(|net| net.smoltcp_net.clone()), entries: entries.clone(), + #[cfg(feature = "kcp")] kcp_endpoint: kcp_endpoint.clone(), peer_mgr: peer_manager.clone(), src_addr: addr, @@ -811,6 +835,7 @@ impl Socks5Server { let tasks = Arc::new(std::sync::Mutex::new(JoinSet::new())); join_joinset_background(tasks.clone(), "tcp port forward".to_string()); let forward_tasks = tasks; + #[cfg(feature = "kcp")] let kcp_endpoint = self.kcp_endpoint.lock().await.clone(); let peer_mgr = self.peer_manager.clone(); let cancel_token = CancellationToken::new(); @@ -843,6 +868,7 @@ impl Socks5Server { ); let connector = Socks5AutoConnector { + #[cfg(feature = "kcp")] kcp_endpoint: kcp_endpoint.clone(), peer_mgr: peer_mgr.clone(), entries: entries.clone(), diff --git a/easytier/src/gateway/tcp_proxy.rs b/easytier/src/gateway/tcp_proxy.rs index b6a5c731..877a2a0b 100644 --- a/easytier/src/gateway/tcp_proxy.rs +++ b/easytier/src/gateway/tcp_proxy.rs @@ -201,6 +201,7 @@ impl ProxyTcpStream { } } +#[cfg(feature = "smoltcp")] type SmolTcpAcceptResult = Result<(tokio_smoltcp::TcpStream, SocketAddr)>; #[cfg(feature = "smoltcp")] struct SmolTcpListener { @@ -331,6 +332,7 @@ pub struct TcpProxy { smoltcp_stack_receiver: Arc>>>, #[cfg(feature = "smoltcp")] smoltcp_net: Arc>>, + #[cfg(feature = "smoltcp")] smoltcp_listener_tx: std::sync::Mutex>>, enable_smoltcp: Arc, @@ -461,6 +463,7 @@ impl TcpProxy { #[cfg(feature = "smoltcp")] smoltcp_net: Arc::new(Mutex::new(None)), + #[cfg(feature = "smoltcp")] smoltcp_listener_tx: std::sync::Mutex::new(None), enable_smoltcp: Arc::new(AtomicBool::new(true)), @@ -930,6 +933,7 @@ impl TcpProxy { tracing::info!(src = ?src, ?real_dst, ?mapped_dst, old_entry = ?old_val, "tcp syn received"); // if smoltcp is enabled, add the listener to the net + #[cfg(feature = "smoltcp")] if self.is_smoltcp_enabled() { let smoltcp_listener_tx = self.smoltcp_listener_tx.lock().unwrap().clone().unwrap(); SmolTcpListener::add_listener( diff --git a/easytier/src/gateway/wrapped_proxy.rs b/easytier/src/gateway/wrapped_proxy.rs new file mode 100644 index 00000000..35d94343 --- /dev/null +++ b/easytier/src/gateway/wrapped_proxy.rs @@ -0,0 +1,150 @@ +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::Arc, +}; + +use pnet::packet::{ + ip::IpNextHeaderProtocols, + ipv4::Ipv4Packet, + tcp::{TcpFlags, TcpPacket}, + Packet as _, +}; +use tokio::io::{copy_bidirectional, AsyncRead, AsyncWrite}; +use tokio_util::io::InspectReader; + +use crate::{ + common::{acl_processor::PacketInfo, error::Result}, + gateway::tcp_proxy::{NatDstConnector, TcpProxy}, + peers::{acl_filter::AclFilter, NicPacketFilter}, + proto::{ + acl::{Action, ChainType}, + api::instance::TcpProxyEntryTransportType, + }, + tunnel::packet_def::ZCPacket, +}; + +#[derive(Clone)] +pub struct ProxyAclHandler { + pub acl_filter: Arc, + pub packet_info: PacketInfo, + pub chain_type: ChainType, +} + +impl ProxyAclHandler { + pub fn handle_packet(&self, buf: &[u8]) -> Result<()> { + let mut packet_info = self.packet_info.clone(); + packet_info.packet_size = buf.len(); + let ret = self + .acl_filter + .get_processor() + .process_packet(&packet_info, self.chain_type); + self.acl_filter.handle_acl_result( + &ret, + &packet_info, + self.chain_type, + &self.acl_filter.get_processor(), + ); + if !matches!(ret.action, Action::Allow) { + return Err(anyhow::anyhow!("acl denied").into()); + } + + Ok(()) + } + + pub async fn copy_bidirection_with_acl( + &self, + src: impl AsyncRead + AsyncWrite + Unpin, + mut dst: impl AsyncRead + AsyncWrite + Unpin, + ) -> Result<()> { + let (src_reader, src_writer) = tokio::io::split(src); + let src_reader = InspectReader::new(src_reader, |buf| { + let _ = self.handle_packet(buf); + }); + let mut src = tokio::io::join(src_reader, src_writer); + + copy_bidirectional(&mut src, &mut dst).await?; + Ok(()) + } +} + +#[async_trait::async_trait] +pub(crate) trait TcpProxyForWrappedSrcTrait: Send + Sync + 'static { + type Connector: NatDstConnector; + fn get_tcp_proxy(&self) -> &Arc>; + async fn check_dst_allow_wrapped_input(&self, dst_ip: &Ipv4Addr) -> bool; +} + +#[async_trait::async_trait] +impl> NicPacketFilter for T { + async fn try_process_packet_from_nic(&self, zc_packet: &mut ZCPacket) -> bool { + let ret = self + .get_tcp_proxy() + .try_process_packet_from_nic(zc_packet) + .await; + if ret { + return true; + } + + let data = zc_packet.payload(); + let ip_packet = Ipv4Packet::new(data).unwrap(); + if ip_packet.get_version() != 4 + || ip_packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp + { + return false; + } + + // if no connection is established, only allow SYN packet + let tcp_packet = TcpPacket::new(ip_packet.payload()).unwrap(); + let is_syn = tcp_packet.get_flags() & TcpFlags::SYN != 0 + && tcp_packet.get_flags() & TcpFlags::ACK == 0; + if is_syn { + // only check dst feature flag when SYN packet + if !self + .check_dst_allow_wrapped_input(&ip_packet.get_destination()) + .await + { + tracing::warn!( + "{:?} proxy src: dst {} not allow wrapped input", + self.get_tcp_proxy().get_transport_type(), + ip_packet.get_destination() + ); + return false; + } + } else { + // if not syn packet, only allow established connection + if !self + .get_tcp_proxy() + .is_tcp_proxy_connection(SocketAddr::new( + IpAddr::V4(ip_packet.get_source()), + tcp_packet.get_source(), + )) + { + return false; + } + } + + if let Some(my_ipv4) = self.get_tcp_proxy().get_global_ctx().get_ipv4() { + // this is a net-to-net packet, only allow it when smoltcp is enabled + // because the syn-ack packet will not be through and handled by the tun device when + // the source ip is in the local network + if ip_packet.get_source() != my_ipv4.address() + && !self.get_tcp_proxy().is_smoltcp_enabled() + { + tracing::warn!( + "{:?} nat 2 nat packet, src: {} dst: {} not allow wrapped input", + self.get_tcp_proxy().get_transport_type(), + ip_packet.get_source(), + ip_packet.get_destination() + ); + return false; + } + }; + + let hdr = zc_packet.mut_peer_manager_header().unwrap(); + hdr.to_peer_id = self.get_tcp_proxy().get_my_peer_id().into(); + if self.get_tcp_proxy().get_transport_type() == TcpProxyEntryTransportType::Kcp { + hdr.set_kcp_src_modified(true); + } + true + } +} diff --git a/easytier/src/instance/dns_server/mod.rs b/easytier/src/instance/dns_server/mod.rs index 3c9cdab4..1bec8c06 100644 --- a/easytier/src/instance/dns_server/mod.rs +++ b/easytier/src/instance/dns_server/mod.rs @@ -1,13 +1,19 @@ // This module is copy and modified from https://github.com/fanyang89/libdns +#[cfg(feature = "magic-dns")] pub(crate) mod config; +#[cfg(feature = "magic-dns")] pub(crate) mod server; +#[cfg(feature = "magic-dns")] pub mod client_instance; +#[cfg(feature = "magic-dns")] pub mod runner; +#[cfg(feature = "magic-dns")] pub mod server_instance; +#[cfg(feature = "magic-dns")] pub mod system_config; -#[cfg(test)] +#[cfg(all(test, feature = "tun", feature = "magic-dns"))] mod tests; pub static MAGIC_DNS_INSTANCE_ADDR: &str = "tcp://127.0.0.1:49813"; diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 9bdb68a7..623a5b0b 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -1,16 +1,20 @@ +#[cfg(feature = "tun")] use std::any::Any; use std::collections::HashSet; use std::net::{IpAddr, Ipv4Addr}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; +#[cfg(feature = "tun")] use std::time::Duration; use anyhow::Context; use cidr::{IpCidr, Ipv4Inet}; use futures::FutureExt; -use tokio::sync::{oneshot, Notify}; -use tokio::{sync::Mutex, task::JoinSet}; +use tokio::sync::{Mutex, Notify}; +#[cfg(feature = "tun")] +use tokio::{sync::oneshot, task::JoinSet}; +#[cfg(feature = "magic-dns")] use tokio_util::sync::CancellationToken; use crate::common::acl_processor::AclRuleBuilder; @@ -24,15 +28,19 @@ use crate::connector::manual::{ConnectorManagerRpcService, ManualConnectorManage use crate::connector::tcp_hole_punch::TcpHolePunchConnector; use crate::connector::udp_hole_punch::UdpHolePunchConnector; use crate::gateway::icmp_proxy::IcmpProxy; +#[cfg(feature = "kcp")] use crate::gateway::kcp_proxy::{KcpProxyDst, KcpProxyDstRpcService, KcpProxySrc}; +#[cfg(feature = "quic")] use crate::gateway::quic_proxy::{QUICProxyDst, QUICProxyDstRpcService, QUICProxySrc}; use crate::gateway::tcp_proxy::{NatDstTcpConnector, TcpProxy, TcpProxyRpcService}; use crate::gateway::udp_proxy::UdpProxy; use crate::peer_center::instance::PeerCenterInstance; use crate::peers::peer_conn::PeerConnId; use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; +#[cfg(feature = "tun")] +use crate::peers::recv_packet_from_chan; use crate::peers::rpc_service::PeerManagerRpcService; -use crate::peers::{create_packet_recv_chan, recv_packet_from_chan, PacketRecvChanReceiver}; +use crate::peers::{create_packet_recv_chan, PacketRecvChanReceiver}; use crate::proto::api::config::{ ConfigPatchAction, ConfigRpc, GetConfigRequest, GetConfigResponse, PatchConfigRequest, PatchConfigResponse, PortForwardPatch, @@ -53,8 +61,8 @@ use crate::rpc_service::InstanceRpcService; use crate::utils::weak_upgrade; use crate::vpn_portal::{self, VpnPortal}; -use super::dns_server::runner::DnsRunner; -use super::dns_server::MAGIC_DNS_FAKE_IP; +#[cfg(feature = "magic-dns")] +use super::dns_server::{runner::DnsRunner, MAGIC_DNS_FAKE_IP}; use super::listeners::ListenerManager; #[cfg(feature = "socks5")] @@ -120,35 +128,31 @@ impl IpProxy { #[cfg(feature = "tun")] type NicCtx = super::virtual_nic::NicCtx; -#[cfg(not(feature = "tun"))] -struct NicCtx; -#[cfg(not(feature = "tun"))] -impl NicCtx { - pub fn new( - _global_ctx: ArcGlobalCtx, - _peer_manager: &Arc, - _peer_packet_receiver: Arc>, - ) -> Self { - Self - } - - pub async fn run(&mut self, _ipv4_addr: Ipv4Addr) -> Result<(), Error> { - Ok(()) - } -} +#[cfg(feature = "magic-dns")] struct MagicDnsContainer { dns_runner_task: ScopedTask<()>, dns_runner_cancel_token: CancellationToken, } // nic container will be cleared when dhcp ip changed +#[cfg(feature = "tun")] pub struct NicCtxContainer { nic_ctx: Option>, + #[cfg(feature = "magic-dns")] magic_dns: Option, } +#[cfg(feature = "tun")] impl NicCtxContainer { + #[cfg(not(feature = "magic-dns"))] + fn new(nic_ctx: NicCtx) -> Self { + Self { + nic_ctx: Some(Box::new(nic_ctx)), + } + } + + #[cfg(feature = "magic-dns")] fn new(nic_ctx: NicCtx, dns_runner: Option) -> Self { if let Some(mut dns_runner) = dns_runner { let token = CancellationToken::new(); @@ -174,11 +178,13 @@ impl NicCtxContainer { fn new_with_any(ctx: T) -> Self { Self { nic_ctx: Some(Box::new(ctx)), + #[cfg(feature = "magic-dns")] magic_dns: None, } } } +#[cfg(feature = "tun")] type ArcNicCtx = Arc>>; pub struct InstanceRpcServerHook { @@ -236,6 +242,7 @@ impl RpcServerHook for InstanceRpcServerHook { #[derive(Clone)] pub struct InstanceConfigPatcher { global_ctx: Weak, + #[cfg(feature = "socks5")] socks5_server: Weak, peer_manager: Weak, conn_manager: Weak, @@ -313,6 +320,7 @@ impl InstanceConfigPatcher { if port_forwards.is_empty() { return Ok(()); } + #[cfg(feature = "socks5")] let Some(socks5_server) = self.socks5_server.upgrade() else { return Err(anyhow::anyhow!("socks5 server not available")); }; @@ -326,6 +334,7 @@ impl InstanceConfigPatcher { global_ctx .config .set_port_forwards(current_forwards.clone()); + #[cfg(feature = "socks5")] socks5_server .reload_port_forwards(¤t_forwards) .await @@ -513,6 +522,7 @@ pub struct Instance { id: uuid::Uuid, + #[cfg(feature = "tun")] nic_ctx: ArcNicCtx, peer_packet_receiver: Arc>, @@ -525,10 +535,14 @@ pub struct Instance { ip_proxy: Option, + #[cfg(feature = "kcp")] kcp_proxy_src: Option, + #[cfg(feature = "kcp")] kcp_proxy_dst: Option, + #[cfg(feature = "quic")] quic_proxy_src: Option, + #[cfg(feature = "quic")] quic_proxy_dst: Option, peer_center: Arc, @@ -596,6 +610,7 @@ impl Instance { id, peer_packet_receiver: Arc::new(Mutex::new(peer_packet_receiver)), + #[cfg(feature = "tun")] nic_ctx: Arc::new(Mutex::new(None)), peer_manager, @@ -606,10 +621,14 @@ impl Instance { tcp_hole_puncher: Arc::new(Mutex::new(tcp_hole_puncher)), ip_proxy: None, + #[cfg(feature = "kcp")] kcp_proxy_src: None, + #[cfg(feature = "kcp")] kcp_proxy_dst: None, + #[cfg(feature = "quic")] quic_proxy_src: None, + #[cfg(feature = "quic")] quic_proxy_dst: None, peer_center, @@ -639,10 +658,12 @@ impl Instance { } // use a mock nic ctx to consume packets. + #[cfg(feature = "tun")] async fn clear_nic_ctx( arc_nic_ctx: ArcNicCtx, packet_recv: Arc>, ) { + #[cfg(feature = "magic-dns")] if let Some(old_ctx) = arc_nic_ctx.lock().await.take() { if let Some(dns_runner) = old_ctx.magic_dns { dns_runner.dns_runner_cancel_token.cancel(); @@ -667,6 +688,7 @@ impl Instance { tracing::debug!("nic ctx cleared."); } + #[cfg(feature = "magic-dns")] fn create_magic_dns_runner( peer_mgr: Arc, tun_dev: Option, @@ -686,13 +708,18 @@ impl Instance { Some(runner) } + #[cfg(feature = "tun")] async fn use_new_nic_ctx( arc_nic_ctx: ArcNicCtx, nic_ctx: NicCtx, - magic_dns: Option, + #[cfg(feature = "magic-dns")] magic_dns: Option, ) { let mut g = arc_nic_ctx.lock().await; - *g = Some(NicCtxContainer::new(nic_ctx, magic_dns)); + *g = Some(NicCtxContainer::new( + nic_ctx, + #[cfg(feature = "magic-dns")] + magic_dns, + )); tracing::debug!("nic ctx updated."); } @@ -701,6 +728,7 @@ impl Instance { use rand::Rng; let peer_manager_c = Arc::downgrade(&self.peer_manager.clone()); let global_ctx_c = self.get_global_ctx(); + #[cfg(feature = "tun")] let nic_ctx = self.nic_ctx.clone(); let _peer_packet_receiver = self.peer_packet_receiver.clone(); tokio::spawn(async move { @@ -765,6 +793,7 @@ impl Instance { "dhcp start changing ip" ); + #[cfg(feature = "tun")] Self::clear_nic_ctx(nic_ctx.clone(), _peer_packet_receiver.clone()).await; if let Some(ip) = candidate_ipv4_addr { @@ -776,11 +805,10 @@ impl Instance { continue; } - #[cfg(not(any( - target_os = "android", - target_os = "ios", - target_env = "ohos" - )))] + #[cfg(all( + not(any(target_os = "android", target_os = "ios", target_env = "ohos")), + feature = "tun" + ))] { let mut new_nic_ctx = NicCtx::new( global_ctx_c.clone(), @@ -798,10 +826,12 @@ impl Instance { global_ctx_c.set_ipv4(None); continue; } + #[cfg(feature = "magic-dns")] let ifname = new_nic_ctx.ifname().await; Self::use_new_nic_ctx( nic_ctx.clone(), new_nic_ctx, + #[cfg(feature = "magic-dns")] Self::create_magic_dns_runner(peer_manager_c.clone(), ifname, ip), ) .await; @@ -819,6 +849,10 @@ impl Instance { }); } + #[cfg(all( + not(any(target_os = "android", target_os = "ios", target_env = "ohos")), + feature = "tun" + ))] fn check_for_static_ip(&self, first_round_output: oneshot::Sender>) { let ipv4_addr = self.global_ctx.get_ipv4(); let ipv6_addr = self.global_ctx.get_ipv6(); @@ -862,15 +896,20 @@ impl Instance { tokio::time::sleep(Duration::from_secs(1)).await; continue; } - let ifname = new_nic_ctx.ifname().await; // Create Magic DNS runner only if we have IPv4 - let dns_runner = if let Some(ipv4) = ipv4_addr { - Self::create_magic_dns_runner(peer_manager, ifname, ipv4) - } else { - None - }; - Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx, dns_runner).await; + #[cfg(feature = "magic-dns")] + { + let ifname = new_nic_ctx.ifname().await; + let dns_runner = if let Some(ipv4) = ipv4_addr { + Self::create_magic_dns_runner(peer_manager, ifname, ipv4) + } else { + None + }; + Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx, dns_runner).await; + } + #[cfg(not(feature = "magic-dns"))] + Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await; if let Some(output_tx) = output_tx.take() { let _ = output_tx.send(Ok(())); @@ -888,6 +927,7 @@ impl Instance { }); } + #[cfg(feature = "quic")] async fn run_quic_dst(&mut self) -> Result<(), Error> { if self.global_ctx.get_flags().disable_quic_input { return Ok(()); @@ -911,11 +951,12 @@ impl Instance { self.listener_manager.lock().await.run().await?; self.peer_manager.run().await?; - Self::clear_nic_ctx(self.nic_ctx.clone(), self.peer_packet_receiver.clone()).await; + #[cfg(feature = "tun")] + { + Self::clear_nic_ctx(self.nic_ctx.clone(), self.peer_packet_receiver.clone()).await; - if !self.global_ctx.config.get_flags().no_tun { #[cfg(not(any(target_os = "android", target_os = "ios", 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); output_rx.await.unwrap()?; @@ -926,24 +967,28 @@ impl Instance { self.check_dhcp_ip_conflict(); } + #[cfg(feature = "kcp")] if self.global_ctx.get_flags().enable_kcp_proxy { let src_proxy = KcpProxySrc::new(self.get_peer_manager()).await; src_proxy.start().await; self.kcp_proxy_src = Some(src_proxy); } + #[cfg(feature = "kcp")] if !self.global_ctx.get_flags().disable_kcp_input { let mut dst_proxy = KcpProxyDst::new(self.get_peer_manager()).await; dst_proxy.start().await; self.kcp_proxy_dst = Some(dst_proxy); } + #[cfg(feature = "quic")] if self.global_ctx.get_flags().enable_quic_proxy { let quic_src = QUICProxySrc::new(self.get_peer_manager()).await; quic_src.start().await; self.quic_proxy_src = Some(quic_src); } + #[cfg(feature = "quic")] if !self.global_ctx.get_flags().disable_quic_input { if let Err(e) = self.run_quic_dst().await { eprintln!( @@ -989,6 +1034,7 @@ impl Instance { #[cfg(feature = "socks5")] self.socks5_server .run( + #[cfg(feature = "kcp")] self.kcp_proxy_src .as_ref() .map(|x| Arc::downgrade(&x.get_kcp_endpoint())), @@ -1126,6 +1172,7 @@ impl Instance { #[derive(Clone)] pub struct PortForwardManagerRpcService { global_ctx: Weak, + #[cfg(feature = "socks5")] socks5_server: Weak, } @@ -1146,6 +1193,7 @@ impl Instance { PortForwardManagerRpcService { global_ctx: Arc::downgrade(&self.global_ctx), + #[cfg(feature = "socks5")] socks5_server: Arc::downgrade(&self.socks5_server), } } @@ -1209,6 +1257,7 @@ impl Instance { pub fn get_config_patcher(&self) -> InstanceConfigPatcher { InstanceConfigPatcher { global_ctx: Arc::downgrade(&self.global_ctx), + #[cfg(feature = "socks5")] socks5_server: Arc::downgrade(&self.socks5_server), peer_manager: Arc::downgrade(&self.peer_manager), conn_manager: Arc::downgrade(&self.conn_manager), @@ -1357,6 +1406,7 @@ impl Instance { Arc::new(TcpProxyRpcService::new(ip_proxy.tcp_proxy.clone())), ); } + #[cfg(feature = "kcp")] if let Some(kcp_proxy) = self.kcp_proxy_src.as_ref() { tcp_proxy_rpc_services.insert( "kcp_src".to_string(), @@ -1364,6 +1414,7 @@ impl Instance { ); } + #[cfg(feature = "kcp")] if let Some(kcp_proxy) = self.kcp_proxy_dst.as_ref() { tcp_proxy_rpc_services.insert( "kcp_dst".to_string(), @@ -1371,6 +1422,7 @@ impl Instance { ); } + #[cfg(feature = "quic")] if let Some(quic_proxy) = self.quic_proxy_src.as_ref() { tcp_proxy_rpc_services.insert( "quic_src".to_string(), @@ -1378,6 +1430,7 @@ impl Instance { ); } + #[cfg(feature = "quic")] if let Some(quic_proxy) = self.quic_proxy_dst.as_ref() { tcp_proxy_rpc_services.insert( "quic_dst".to_string(), @@ -1402,6 +1455,7 @@ impl Instance { self.vpn_portal.clone() } + #[cfg(feature = "tun")] pub fn get_nic_ctx(&self) -> ArcNicCtx { self.nic_ctx.clone() } @@ -1446,6 +1500,7 @@ impl Instance { pub async fn clear_resources(&mut self) { self.peer_manager.clear_resources().await; + #[cfg(feature = "tun")] let _ = self.nic_ctx.lock().await.take(); } } @@ -1454,8 +1509,10 @@ impl Drop for Instance { fn drop(&mut self) { let my_peer_id = self.peer_manager.my_peer_id(); let pm = Arc::downgrade(&self.peer_manager); + #[cfg(feature = "tun")] let nic_ctx = self.nic_ctx.clone(); tokio::spawn(async move { + #[cfg(feature = "tun")] nic_ctx.lock().await.take(); if let Some(pm) = pm.upgrade() { pm.clear_resources().await; diff --git a/easytier/src/proto/mod.rs b/easytier/src/proto/mod.rs index ba3a16ee..bffca9b0 100644 --- a/easytier/src/proto/mod.rs +++ b/easytier/src/proto/mod.rs @@ -5,6 +5,7 @@ pub mod acl; pub mod api; pub mod common; pub mod error; +#[cfg(feature = "magic-dns")] pub mod magic_dns; pub mod peer_rpc; pub mod web;