diff --git a/Cargo.lock b/Cargo.lock index e62b068..2ab6bf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,18 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "attohttpc" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" +dependencies = [ + "http", + "log", + "url", + "wildmatch", +] + [[package]] name = "autocfg" version = "1.2.0" @@ -513,6 +525,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -604,6 +625,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "humantime" version = "2.1.0" @@ -633,6 +665,29 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "igd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556b5a75cd4adb7c4ea21c64af1c48cefb2ce7d43dc4352c720a1fe47c21f355" +dependencies = [ + "attohttpc", + "log", + "rand", + "url", + "xmltree", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -861,11 +916,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "log", "wasi", "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4929e1f84c5e54c3ec6141cd5d8b5a5c055f031f80cf78f2072920173cb4d880" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "nom" version = "7.1.3" @@ -1033,6 +1100,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1614,6 +1687,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.37.0" @@ -1623,7 +1711,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 0.8.11", "num_cpus", "parking_lot", "pin-project-lite", @@ -1672,12 +1760,27 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.11" @@ -1715,6 +1818,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.8.0" @@ -1777,12 +1891,13 @@ dependencies = [ "dns-parser", "ecb", "fnv", + "igd", "libc", "libloading", "libsm", "log", "lz4_flex", - "mio", + "mio 1.0.0", "openssl-sys", "packet", "parking_lot", @@ -1895,6 +2010,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +[[package]] +name = "wildmatch" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" + [[package]] name = "winapi" version = "0.3.9" @@ -2065,6 +2186,21 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + [[package]] name = "yasna" version = "0.5.2" diff --git a/common/Cargo.toml b/common/Cargo.toml index f29dc1d..60a600d 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -32,7 +32,7 @@ ip_proxy = ["vnt/ip_proxy"] port_mapping = ["vnt/port_mapping"] lz4 = ["vnt/lz4_compress"] zstd = ["vnt/zstd_compress"] - +upnp = ["vnt/upnp"] command = [] file_config = [] log = ["log4rs"] diff --git a/vnt-cli/Cargo.toml b/vnt-cli/Cargo.toml index 7b099ca..f4dfaa4 100644 --- a/vnt-cli/Cargo.toml +++ b/vnt-cli/Cargo.toml @@ -39,7 +39,7 @@ ip_proxy = ["vnt/ip_proxy", "common/ip_proxy"] log = ["common/log"] command = ["common/command"] file_config = ["common/file_config"] - +upnp = ["common/upnp"] [build-dependencies] rand = "0.8.5" diff --git a/vnt/Cargo.toml b/vnt/Cargo.toml index 684c471..aa2c7c1 100644 --- a/vnt/Cargo.toml +++ b/vnt/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tun = { path = "tun" ,optional = true} +tun = { path = "tun", optional = true } packet = { path = "./packet" } bytes = "1.5.0" log = "0.4.17" @@ -32,7 +32,7 @@ spki = { version = "0.7.2", features = ["fingerprint", "alloc", "base64"], optio openssl-sys = { git = "https://github.com/lbl8603/rust-openssl", optional = true } libsm = { git = "https://github.com/lbl8603/libsm", optional = true } -mio = { version = "0.8.10", features = ["os-poll", "net", "os-ext"] } +mio = { version = "1.0.0", features = ["os-poll", "net", "os-ext"] } crossbeam-queue = "0.3.11" anyhow = "1.0.82" dns-parser = "0.8.0" @@ -43,6 +43,7 @@ lz4_flex = { version = "0.11", default-features = false, optional = true } zstd = { version = "0.13.1", optional = true } fnv = "1.0.7" +igd = { version = "0.12.1", optional = true } [target.'cfg(target_os = "windows")'.dependencies] libloading = "0.8.0" @@ -53,7 +54,7 @@ protoc-bin-vendored = "3.0.0" cfg_aliases = "0.2.1" [features] -default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress","integrated_tun"] +default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress", "integrated_tun"] openssl = ["openssl-sys"] # 从源码编译 openssl-vendored = ["openssl-sys/vendored"] @@ -69,3 +70,4 @@ port_mapping = ["tokio"] lz4_compress = ["lz4_flex"] zstd_compress = ["zstd"] integrated_tun = ["tun"] +upnp = ["igd"] diff --git a/vnt/src/handle/maintain/re_nat_type.rs b/vnt/src/handle/maintain/re_nat_type.rs index 975f4b2..e8eca7d 100644 --- a/vnt/src/handle/maintain/re_nat_type.rs +++ b/vnt/src/handle/maintain/re_nat_type.rs @@ -42,6 +42,9 @@ fn retrieve_nat_type0( log::warn!("nat re_test {:?}", e); } }; + #[cfg(feature = "upnp")] + nat_test.reset_upnp(); + log::info!("刷新nat成功") } }) .expect("natTest"); diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs index 786ae96..d35e1e8 100644 --- a/vnt/src/nat/mod.rs +++ b/vnt/src/nat/mod.rs @@ -13,6 +13,8 @@ use rand::Rng; use crate::channel::punch::{NatInfo, NatType}; use crate::proto::message::PunchNatType; +#[cfg(feature = "upnp")] +use crate::util::UPnP; mod stun; @@ -111,6 +113,8 @@ pub struct NatTest { time: Arc>, udp_ports: Vec, tcp_port: u16, + #[cfg(feature = "upnp")] + upnp: UPnP, } impl From for PunchNatType { @@ -157,6 +161,14 @@ impl NatTest { NatType::Cone, ); let info = Arc::new(Mutex::new(nat_info)); + #[cfg(feature = "upnp")] + let upnp = UPnP::default(); + #[cfg(feature = "upnp")] + for port in &udp_ports { + upnp.add_udp_port(*port); + } + #[cfg(feature = "upnp")] + upnp.add_tcp_port(tcp_port); NatTest { stun_server, info, @@ -165,6 +177,8 @@ impl NatTest { )), udp_ports, tcp_port, + #[cfg(feature = "upnp")] + upnp, } } pub fn can_update(&self) -> bool { @@ -255,6 +269,13 @@ impl NatTest { Ok(guard.clone()) } + #[cfg(feature = "upnp")] + pub fn reset_upnp(&self) { + let local_ipv4 = self.info.lock().local_ipv4.clone(); + if let Some(local_ipv4) = local_ipv4 { + self.upnp.reset(local_ipv4) + } + } pub fn send_data(&self) -> anyhow::Result<(Vec, SocketAddr)> { let len = self.stun_server.len(); let stun_server = if len == 1 { @@ -297,7 +318,7 @@ impl NatTest { let source_ip = match source_addr.ip() { IpAddr::V4(ip) => ip, IpAddr::V6(ip) => { - if let Some(ip) = ip.to_ipv4_mapped() { + if let Some(ip) = ip.to_ipv4() { ip } else { return Ok(()); diff --git a/vnt/src/util/mod.rs b/vnt/src/util/mod.rs index 0069e2d..40a4cec 100644 --- a/vnt/src/util/mod.rs +++ b/vnt/src/util/mod.rs @@ -8,3 +8,8 @@ pub use counter::*; mod dns_query; pub use dns_query::*; + +#[cfg(feature = "upnp")] +mod upnp; +#[cfg(feature = "upnp")] +pub use upnp::*; diff --git a/vnt/src/util/upnp.rs b/vnt/src/util/upnp.rs new file mode 100644 index 0000000..ae3755d --- /dev/null +++ b/vnt/src/util/upnp.rs @@ -0,0 +1,81 @@ +use igd::{search_gateway, PortMappingProtocol}; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::ops::Deref; +use std::sync::Arc; + +use parking_lot::Mutex; + +#[derive(Clone, Default)] +pub struct UPnP { + inner: Arc, +} + +impl Deref for UPnP { + type Target = UpnpInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[derive(Default)] +pub struct UpnpInner { + list: Mutex>, +} + +impl UpnpInner { + pub fn add_tcp_port(&self, port: u16) { + self.list.lock().push((PortMappingProtocol::TCP, port)); + } + pub fn add_udp_port(&self, port: u16) { + self.list.lock().push((PortMappingProtocol::UDP, port)); + } + pub fn reset(&self, local_ip: Ipv4Addr) { + let gateway = match search_gateway(Default::default()) { + Ok(gateway) => gateway, + Err(e) => { + log::warn!("search_gateway {:?}", e); + return; + } + }; + let guard = self.list.lock(); + + // 不支持upnp的情况会阻塞30秒,之后再改这个库 + for (protocol, port) in guard.iter() { + let local_addr = SocketAddrV4::new(local_ip, *port); + log::info!("add upnp protocol={} {}", protocol, local_addr); + if let Err(e) = gateway.add_port(*protocol, *port, local_addr, 700, "upnp") { + log::warn!( + "add upnp failed protocol={},port={} err:{:?}", + protocol, + port, + e + ); + } + } + } +} + +impl Drop for UpnpInner { + fn drop(&mut self) { + // let gateway = match search_gateway(Default::default()) { + // Ok(gateway) => gateway, + // Err(e) => { + // log::warn!("search_gateway {:?}", e); + // return; + // } + // }; + // + // let guard = self.list.lock(); + // for (protocol, port) in guard.iter() { + // if let Err(e) = gateway.remove_port(*protocol, *port) { + // log::warn!( + // "remove upnp failed protocol={},port={} err:{:?}", + // protocol, + // port, + // e + // ); + // } + // } + } +}