From 3b1236cfd0441e34a8dc988b33a5c237ff1bbf74 Mon Sep 17 00:00:00 2001 From: lbl8603 <49143209+lbl8603@users.noreply.github.com> Date: Fri, 10 May 2024 23:18:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E8=BF=87stun=E6=8E=A2=E6=B5=8B?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=EF=BC=8C=E8=A7=A3=E5=86=B3=E4=BD=BF=E7=94=A8?= =?UTF-8?q?ipv6=E6=88=96tcp=E6=9C=8D=E5=8A=A1=E6=97=B6=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnt/src/core/conn.rs | 4 ++ vnt/src/handle/maintain/addr_request.rs | 66 ++++++++++++++++++++----- vnt/src/handle/maintain/mod.rs | 2 +- vnt/src/handle/recv_data/mod.rs | 15 +++++- vnt/src/nat/mod.rs | 66 ++++++++++++++++++++++++- vnt/src/nat/stun.rs | 50 ++++++++++++++++--- 6 files changed, 181 insertions(+), 22 deletions(-) diff --git a/vnt/src/core/conn.rs b/vnt/src/core/conn.rs index 9ed9b68..00308fb 100644 --- a/vnt/src/core/conn.rs +++ b/vnt/src/core/conn.rs @@ -339,6 +339,7 @@ pub fn start( context.clone(), current_device.clone(), server_cipher.clone(), + nat_test.clone(), config_info.clone(), ); // 定时打洞 @@ -420,4 +421,7 @@ impl Vnt { pub fn wait_timeout(&self, dur: Duration) -> bool { self.stop_manager.wait_timeout(dur) } + pub fn config(&self) -> &Config { + &self.config + } } diff --git a/vnt/src/handle/maintain/addr_request.rs b/vnt/src/handle/maintain/addr_request.rs index 05412f5..dc66f64 100644 --- a/vnt/src/handle/maintain/addr_request.rs +++ b/vnt/src/handle/maintain/addr_request.rs @@ -4,8 +4,10 @@ use std::time::Duration; use crossbeam_utils::atomic::AtomicCell; use crate::channel::context::ChannelContext; +use crate::channel::punch::NatType; use crate::cipher::Cipher; use crate::handle::{BaseConfigInfo, CurrentDeviceInfo}; +use crate::nat::NatTest; use crate::protocol::body::ENCRYPTION_RESERVED; use crate::protocol::{control_packet, NetPacket, Protocol, MAX_TTL}; use crate::util::Scheduler; @@ -15,6 +17,7 @@ pub fn addr_request( context: ChannelContext, current_device_info: Arc>, server_cipher: Cipher, + nat_test: NatTest, _config: BaseConfigInfo, ) { pub_address_request( @@ -22,31 +25,67 @@ pub fn addr_request( context, current_device_info.clone(), server_cipher, + nat_test, + 0, ); } -pub fn pub_address_request( + +fn pub_address_request( scheduler: &Scheduler, context: ChannelContext, current_device_info: Arc>, server_cipher: Cipher, + nat_test: NatTest, + count: usize, ) { - addr_request0(&context, ¤t_device_info, &server_cipher); - // 9秒发送一次 - let rs = scheduler.timeout(Duration::from_secs(9), |s| { - pub_address_request(s, context, current_device_info, server_cipher) + let channel_num = context.channel_num(); + let index = count % channel_num; + let mut time = if index == channel_num - 1 { 19 } else { 1 }; + if let Err(e) = addr_request0( + &context, + ¤t_device_info, + &server_cipher, + &nat_test, + index, + ) { + log::warn!("{:?}", e); + } + let nat_info = nat_test.nat_info(); + if nat_info.nat_type == NatType::Symmetric { + //对称网络探测端口没啥作用,把频率放低,(锥形网络也只在打洞前需要探测端口,后续可以改改) + if !nat_info.public_ports.contains(&0) && !nat_info.public_ips.is_empty() { + time = 600; + } + } + + let rs = scheduler.timeout(Duration::from_secs(time), move |s| { + pub_address_request( + s, + context, + current_device_info, + server_cipher, + nat_test, + index + 1, + ) }); if !rs { log::info!("定时任务停止"); } } -pub fn addr_request0( +fn addr_request0( context: &ChannelContext, current_device: &AtomicCell, server_cipher: &Cipher, -) { + nat_test: &NatTest, + index: usize, +) -> anyhow::Result<()> { let current_dev = current_device.load(); - if current_dev.connect_server.is_ipv4() && current_dev.status.online() { + if current_dev.status.offline() { + return Ok(()); + } + + if current_dev.connect_server.is_ipv4() && !context.is_main_tcp() { // 如果连接的是ipv4服务,则探测公网端口 let gateway_ip = current_dev.virtual_gateway; let src_ip = current_dev.virtual_ip; @@ -58,10 +97,11 @@ pub fn addr_request0( packet.first_set_ttl(MAX_TTL); packet.set_source(src_ip); packet.set_destination(gateway_ip); - if let Err(e) = server_cipher.encrypt_ipv4(&mut packet) { - log::warn!("AddrRequest err={:?}", e) - } else { - context.try_send_all_main(packet.buffer(), current_dev.connect_server); - } + server_cipher.encrypt_ipv4(&mut packet)?; + context.send_main_udp(index, packet.buffer(), current_dev.connect_server)?; + } else { + let (data, addr) = nat_test.send_data()?; + context.send_main_udp(index, &data, addr)?; } + Ok(()) } diff --git a/vnt/src/handle/maintain/mod.rs b/vnt/src/handle/maintain/mod.rs index 1055835..5e9dfe0 100644 --- a/vnt/src/handle/maintain/mod.rs +++ b/vnt/src/handle/maintain/mod.rs @@ -6,7 +6,7 @@ mod re_nat_type; pub use re_nat_type::retrieve_nat_type; mod addr_request; -pub use addr_request::addr_request; +pub use addr_request::*; mod punch; pub use punch::*; diff --git a/vnt/src/handle/recv_data/mod.rs b/vnt/src/handle/recv_data/mod.rs index 32420f6..b644eda 100644 --- a/vnt/src/handle/recv_data/mod.rs +++ b/vnt/src/handle/recv_data/mod.rs @@ -39,10 +39,22 @@ pub struct RecvDataHandler { client: ClientPacketHandler, server: ServerPacketHandler, counter: U64Adder, + nat_test: NatTest, } impl RecvChannelHandler for RecvDataHandler { fn handle(&mut self, buf: &mut [u8], route_key: RouteKey, context: &ChannelContext) { + //判断stun响应包 + if !route_key.is_tcp() { + if let Ok(rs) = self + .nat_test + .recv_data(route_key.index(), route_key.addr, buf) + { + if rs { + return; + } + } + } if let Err(e) = self.handle0(buf, route_key, context) { log::error!("[{}]-{:?}", thread::current().name().unwrap_or(""), e); } @@ -86,7 +98,7 @@ impl RecvDataHandler { client_cipher, punch_sender, peer_nat_info_map, - nat_test, + nat_test.clone(), route, #[cfg(feature = "ip_proxy")] ip_proxy_map, @@ -98,6 +110,7 @@ impl RecvDataHandler { client, server, counter, + nat_test, } } fn handle0( diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs index ca28b05..b442c30 100644 --- a/vnt/src/nat/mod.rs +++ b/vnt/src/nat/mod.rs @@ -1,5 +1,6 @@ +use anyhow::Context; use std::io; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; use std::net::{SocketAddr, UdpSocket}; use std::ops::Sub; use std::sync::Arc; @@ -7,6 +8,7 @@ use std::time::{Duration, Instant}; use crossbeam_utils::atomic::AtomicCell; use parking_lot::Mutex; +use rand::Rng; use crate::channel::punch::{NatInfo, NatType}; use crate::proto::message::PunchNatType; @@ -202,4 +204,66 @@ impl NatTest { Ok(guard.clone()) } + pub fn send_data(&self) -> anyhow::Result<(Vec, SocketAddr)> { + let len = self.stun_server.len(); + let stun_server = if len == 1 { + &self.stun_server[0] + } else { + let index = rand::thread_rng().gen_range(0..self.stun_server.len()); + &self.stun_server[index] + }; + let addr = stun_server + .to_socket_addrs()? + .next() + .with_context(|| format!("stun error {:?}", stun_server))?; + Ok((stun::send_stun_request(), addr)) + } + pub fn recv_data( + &self, + index: usize, + source_addr: SocketAddr, + buf: &[u8], + ) -> anyhow::Result { + if let Some(addr) = stun::recv_stun_response(buf) { + if let SocketAddr::V4(addr) = addr { + let mut check_fail = true; + let source_ip = match source_addr.ip() { + IpAddr::V4(ip) => ip, + IpAddr::V6(ip) => { + if let Some(ip) = ip.to_ipv4_mapped() { + ip + } else { + return Ok(false); + } + } + }; + 'a: for stun_server in &self.stun_server { + for x in stun_server.to_socket_addrs()? { + if source_addr.port() == x.port() { + if let IpAddr::V4(ip) = x.ip() { + if ip == source_ip { + check_fail = false; + break 'a; + } + }; + } + } + } + if check_fail { + return Ok(false); + } + let ip = addr.ip(); + if !ip.is_multicast() + && !ip.is_broadcast() + && !ip.is_unspecified() + && !ip.is_loopback() + && !ip.is_private() + { + self.update_addr(index, *addr.ip(), addr.port()); + return Ok(true); + } + } + } + return Ok(false); + } } diff --git a/vnt/src/nat/stun.rs b/vnt/src/nat/stun.rs index 08b5cba..ef4a636 100644 --- a/vnt/src/nat/stun.rs +++ b/vnt/src/nat/stun.rs @@ -4,6 +4,7 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::time::Duration; use crate::channel::punch::NatType; +use rand::RngCore; use std::net::UdpSocket; use stun_format::Attr; @@ -79,8 +80,7 @@ pub fn stun_test_nat0(stun_servers: Vec) -> io::Result<(NatType, Vec io::Result> { udp.connect(stun_server)?; - // 随便搞个当id - let tid = stun_server.as_ptr() as u128; + let tid = rand::thread_rng().next_u64() as u128; let mut addr = HashSet::new(); let (mapped_addr1, changed_addr1) = test_nat_(&udp, true, true, tid)?; if mapped_addr1.is_ipv4() { @@ -119,13 +119,12 @@ fn test_nat_( for _ in 0..2 { let mut buf = [0u8; 28]; let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice()); - msg.typ(stun_format::MsgType::BindingRequest).unwrap(); - msg.tid(tid).unwrap(); + msg.typ(stun_format::MsgType::BindingRequest); + msg.tid(tid); msg.add_attr(Attr::ChangeRequest { change_ip, change_port, - }) - .unwrap(); + }); udp.send(msg.as_bytes())?; let mut buf = [0; 10240]; let (len, _addr) = match udp.recv_from(&mut buf) { @@ -178,3 +177,42 @@ fn stun_addr(addr: stun_format::SocketAddr) -> SocketAddr { } } } + +const TAG: u128 = 1827549368 << 64; + +pub fn send_stun_request() -> Vec { + let mut buf = [0u8; 28]; + let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice()); + msg.typ(stun_format::MsgType::BindingRequest); + let id = rand::thread_rng().next_u64() as u128; + msg.tid(id | TAG); + msg.add_attr(Attr::ChangeRequest { + change_ip: false, + change_port: false, + }); + msg.as_bytes().to_vec() +} + +pub fn recv_stun_response(buf: &[u8]) -> Option { + if buf[0] != 0x01 && buf[1] != 0x01 { + return None; + } + let msg = stun_format::Msg::from(buf); + if let Some(tid) = msg.tid() { + if tid & TAG != TAG { + return None; + } + } + for x in msg.attrs_iter() { + match x { + Attr::MappedAddress(addr) => { + return Some(stun_addr(addr)); + } + Attr::XorMappedAddress(addr) => { + return Some(stun_addr(addr)); + } + _ => {} + } + } + None +}