diff --git a/common/src/cli.rs b/common/src/cli.rs index 27917cc..e137104 100644 --- a/common/src/cli.rs +++ b/common/src/cli.rs @@ -156,9 +156,9 @@ pub fn parse_args_config() -> anyhow::Result, bool)> let mut stun_server = matches.opt_strs("e"); if stun_server.is_empty() { - stun_server.push("stun1.l.google.com:19302".to_string()); - stun_server.push("stun2.l.google.com:19302".to_string()); - stun_server.push("stun.miwifi.com:3478".to_string()); + for x in config::PUB_STUN { + stun_server.push(x.to_string()); + } } let dns = matches.opt_strs("dns"); let in_ip = matches.opt_strs("i"); @@ -340,7 +340,7 @@ fn print_usage(program: &str, _opts: Options) { println!(" -n 给设备一个名字,便于区分不同设备,默认使用系统版本"); println!(" -d 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复"); println!(" -s 注册和中继服务器地址,以'TXT:'开头表示解析TXT记录"); - println!(" -e stun服务器,用于探测NAT类型,可使用多个地址,如-e stun1.l.google.com -e stun2.l.google.com"); + println!(" -e stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com"); #[cfg(target_os = "windows")] #[cfg(feature = "integrated_tun")] println!( diff --git a/common/src/config/file_config.rs b/common/src/config/file_config.rs index ad88861..6d177c7 100644 --- a/common/src/config/file_config.rs +++ b/common/src/config/file_config.rs @@ -2,8 +2,8 @@ use anyhow::anyhow; use std::net::Ipv4Addr; use std::str::FromStr; -use crate::args_parse; use crate::config::get_device_id; +use crate::{args_parse, config}; use serde::{Deserialize, Serialize}; use vnt::channel::punch::PunchModel; use vnt::channel::UseChannelType; @@ -49,6 +49,10 @@ pub struct FileConfig { impl Default for FileConfig { fn default() -> Self { + let mut stun_server = Vec::new(); + for x in config::PUB_STUN { + stun_server.push(x.to_string()); + } Self { #[cfg(target_os = "windows")] tap: false, @@ -59,11 +63,7 @@ impl Default for FileConfig { .unwrap_or("UnknownName") .to_string(), server_address: "nat1.wherewego.top:29872".to_string(), - stun_server: vec![ - "stun1.l.google.com:19302".to_string(), - "stun2.l.google.com:19302".to_string(), - "stun.miwifi.com:3478".to_string(), - ], + stun_server, dns: vec![], in_ips: vec![], out_ips: vec![], diff --git a/common/src/config/mod.rs b/common/src/config/mod.rs index 048b91e..8810c92 100644 --- a/common/src/config/mod.rs +++ b/common/src/config/mod.rs @@ -1,3 +1,9 @@ +pub const PUB_STUN: [&'static str; 4] = [ + "stun.miwifi.com", + "stun.chat.bilibili.com", + "stun.hitv.com", + "stun.cdnbye.com", +]; #[cfg(feature = "file_config")] mod file_config; diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs index 1a75fba..786ae96 100644 --- a/vnt/src/nat/mod.rs +++ b/vnt/src/nat/mod.rs @@ -8,6 +8,7 @@ use std::time::{Duration, Instant}; use crossbeam_utils::atomic::AtomicCell; use parking_lot::Mutex; +use rand::prelude::SliceRandom; use rand::Rng; use crate::channel::punch::{NatInfo, NatType}; @@ -47,12 +48,60 @@ pub fn local_ipv6_() -> io::Result { pub fn local_ipv6() -> Option { match local_ipv6_() { - Ok(ipv6) => Some(ipv6), + Ok(ipv6) => { + if is_ipv6_global(&ipv6) { + return Some(ipv6); + } + } Err(e) => { log::warn!("获取ipv6失败:{:?}", e); - None } } + None +} +pub const fn is_ipv4_global(ipv4: &Ipv4Addr) -> bool { + !(ipv4.octets()[0] == 0 // "This network" + || ipv4.is_private() + || ipv4.octets()[0] == 100 && (ipv4.octets()[1] & 0b1100_0000 == 0b0100_0000)//ipv4.is_shared() + || ipv4.is_loopback() + || ipv4.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + // .9 and .10 are documented as globally reachable so they're excluded + || ( + ipv4.octets()[0] == 192 && ipv4.octets()[1] == 0 && ipv4.octets()[2] == 0 + && ipv4.octets()[3] != 9 && ipv4.octets()[3] != 10 + ) + || ipv4.is_documentation() + || ipv4.octets()[0] == 198 && (ipv4.octets()[1] & 0xfe) == 18//ipv4.is_benchmarking() + || ipv4.octets()[0] & 240 == 240 && !ipv4.is_broadcast()//ipv4.is_reserved() + || ipv4.is_broadcast()) +} +pub const fn is_ipv6_global(ipv6addr: &Ipv6Addr) -> bool { + !(ipv6addr.is_unspecified() + || ipv6addr.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(ipv6addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(ipv6addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(ipv6addr.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(ipv6addr.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(ipv6addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || (ipv6addr.segments()[0] == 0x2001) && (ipv6addr.segments()[1] == 0xdb8)//ipv6addr.is_documentation() + || (ipv6addr.segments()[0] & 0xfe00) == 0xfc00//ipv6addr.is_unique_local() + || (ipv6addr.segments()[0] & 0xffc0) == 0xfe80) //ipv6addr.is_unicast_link_local()) } #[derive(Clone)] @@ -92,7 +141,9 @@ impl NatTest { tcp_port: u16, ) -> NatTest { if stun_server.len() > 5 { + stun_server.shuffle(&mut rand::thread_rng()); stun_server.truncate(5); + log::info!("stun_server truncate {:?}", stun_server); } let ports = vec![0; udp_ports.len()]; let nat_info = NatInfo::new( @@ -266,13 +317,7 @@ impl NatTest { } } if !check_fail { - let ip = addr.ip(); - if !ip.is_multicast() - && !ip.is_broadcast() - && !ip.is_unspecified() - && !ip.is_loopback() - && !ip.is_private() - { + if is_ipv4_global(addr.ip()) { if self.update_addr(index, *addr.ip(), addr.port()) { log::info!("回应地址{:?},来源stun {:?}", addr, source_addr) } diff --git a/vnt/src/nat/stun.rs b/vnt/src/nat/stun.rs index c1af7ff..dcc0e7e 100644 --- a/vnt/src/nat/stun.rs +++ b/vnt/src/nat/stun.rs @@ -9,17 +9,12 @@ use std::net::UdpSocket; use stun_format::Attr; pub fn stun_test_nat(stun_servers: Vec) -> io::Result<(NatType, Vec, u16)> { - let mut th = Vec::new(); - for _ in 0..2 { - let stun_servers = stun_servers.clone(); - let handle = std::thread::spawn(move || stun_test_nat0(stun_servers)); - th.push(handle); - } let mut nat_type = NatType::Cone; let mut port_range = 0; let mut hash_set = HashSet::new(); - for x in th { - match x.join().unwrap() { + for _ in 0..2 { + let stun_servers = stun_servers.clone(); + match stun_test_nat0(stun_servers) { Ok((nat_type_t, ip_list_t, port_range_t)) => { if nat_type_t == NatType::Symmetric { nat_type = NatType::Symmetric; @@ -86,13 +81,13 @@ fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result { if mapped_addr2.is_ipv4() { addr.insert(mapped_addr1); @@ -116,6 +111,7 @@ fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result rs, Err(e) => { - log::warn!("stun error {:?}", e); + log::warn!("stun {} error {:?}", stun_server, e); continue; } };