diff --git a/vnt-cli/src/main.rs b/vnt-cli/src/main.rs index bd3e4bfc..3ee8c8f7 100644 --- a/vnt-cli/src/main.rs +++ b/vnt-cli/src/main.rs @@ -31,15 +31,15 @@ async fn main0() { let args: Vec = std::env::args().collect(); let program = args[0].clone(); let mut opts = Options::new(); - opts.optopt("k", "", &format!("{}", green("必选,使用相同的token,就能组建一个局域网络".to_string())), ""); - opts.optopt("n", "", "给设备一个名字,默认使用系统版本", ""); + opts.optopt("k", "", "必选,使用相同的token,就能组建一个局域网络", ""); + opts.optopt("n", "", "给设备一个名字,便于区分不同设备,默认使用系统版本", ""); opts.optopt("d", "", "设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip", ""); opts.optflag("c", "", "关闭交互式命令,使用此参数禁用控制台输入"); opts.optopt("s", "", "注册和中继服务器地址", ""); - opts.optopt("e", "", "NAT探测服务器地址,使用逗号分隔", ""); + opts.optmulti("e", "", "stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2", ""); opts.optflag("a", "", "使用tap模式,默认使用tun模式"); - opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3 \n表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3", ""); - opts.optmulti("o", "", "配置点对网时使用,-o 192.168.0.0/24 \n表示允许将数据转发到192.168.0.0/24", ""); + opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3 \n表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3,可指定多个网段", ""); + opts.optmulti("o", "", "配置点对网时使用,-o 192.168.0.0/24 \n表示允许将数据转发到192.168.0.0/24,可指定多个网段", ""); opts.optopt("w", "", "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", ""); opts.optflag("m", "", "模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送"); opts.optopt("u", "", "自定义mtu(默认为1430)", ""); @@ -47,11 +47,11 @@ async fn main0() { opts.optopt("", "ip", "指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", ""); opts.optflag("", "relay", "仅使用服务器转发,不使用p2p,默认情况允许使用p2p"); //"后台运行时,查看其他设备列表" - opts.optflag("", "list", &format!("{}", yellow("后台运行时,查看其他设备列表".to_string()))); - opts.optflag("", "all", &format!("{}", yellow("后台运行时,查看其他设备完整信息".to_string()))); - opts.optflag("", "info", &format!("{}", yellow("后台运行时,查看当前设备信息".to_string()))); - opts.optflag("", "route", &format!("{}", yellow("后台运行时,查看数据转发路径".to_string()))); - opts.optflag("", "stop", &format!("{}", yellow("停止后台运行".to_string()))); + opts.optflag("", "list", "后台运行时,查看其他设备列表"); + opts.optflag("", "all", "后台运行时,查看其他设备完整信息"); + opts.optflag("", "info", "后台运行时,查看当前设备信息"); + opts.optflag("", "route", "后台运行时,查看数据转发路径"); + opts.optflag("", "stop", "停止后台运行"); opts.optflag("h", "help", "帮助"); let matches = match opts.parse(&args[1..]) { Ok(m) => { m } @@ -132,11 +132,12 @@ async fn main0() { return; } }; - let nat_test_server = matches.opt_get_default("e", - "nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()).unwrap(); - - let nat_test_server = nat_test_server.split(",").flat_map(|a| a.to_socket_addrs()).flatten() - .collect::>(); + 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.qq.com:3478".to_string()); + } let in_ip = matches.opt_strs("i"); let in_ip = match ips_parse(&in_ip) { @@ -192,7 +193,7 @@ async fn main0() { let config = Config::new(tap, token, device_id, name, server_address, server_address_str, - nat_test_server, in_ip, + stun_server, in_ip, out_ip, password, simulate_multicast, mtu, tcp_channel, virtual_ip, relay); let mut vnt_util = VntUtil::new(config).await.unwrap(); let response = loop { @@ -309,10 +310,33 @@ async fn main0() { vnt.wait_stop().await; } -fn print_usage(program: &str, opts: Options) { - let brief = format!("Usage: {} [options]", program); +fn print_usage(program: &str, _opts: Options) { + println!("Usage: {} [options]", program); println!("version:1.1.2"); - println!("{}", opts.usage(&brief)); + println!("Options:"); + println!(" -k {}", green("必选,使用相同的token,就能组建一个局域网络".to_string())); + println!(" -n 给设备一个名字,便于区分不同设备,默认使用系统版本"); + println!(" -d 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip"); + println!(" -c 关闭交互式命令,使用此参数禁用控制台输入"); + println!(" -s 注册和中继服务器地址"); + println!(" -e stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2"); + println!(" -a 使用tap模式,默认使用tun模式"); + println!(" -i 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据"); + println!(" 并转发到10.26.0.3,可指定多个网段"); + println!(" -o 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段"); + println!(" -w 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信"); + println!(" -m 模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送"); + println!(" -u 自定义mtu(默认为1430)"); + println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp"); + println!(" --ip 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配"); + println!(" --relay 仅使用服务器转发,不使用p2p,默认情况允许使用p2p"); + println!(); + println!(" --list {}",yellow("后台运行时,查看其他设备列表".to_string())); + println!(" --all {}",yellow("后台运行时,查看其他设备完整信息".to_string())); + println!(" --info {}",yellow("后台运行时,查看当前设备信息".to_string())); + println!(" --route {}",yellow("后台运行时,查看数据转发路径".to_string())); + println!(" --stop {}",yellow("停止后台运行".to_string())); + println!(" -h, --help 帮助"); } fn green(str: String) -> impl std::fmt::Display { diff --git a/vnt-jni/src/vnt_util.rs b/vnt-jni/src/vnt_util.rs index e1fe6e05..9bcaf558 100644 --- a/vnt-jni/src/vnt_util.rs +++ b/vnt-jni/src/vnt_util.rs @@ -55,7 +55,7 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result { let device_id = to_string_not_null(env, &config, "deviceId")?; let password = to_string(env, &config, "password")?; let server_address_str = to_string_not_null(env, &config, "server")?; - let nat_test_server = to_string_not_null(env, &config, "natTestServer")?; + // let nat_test_server = to_string_not_null(env, &config, "natTestServer")?; let server_address = match server_address_str.to_socket_addrs() { Ok(mut rs) => { if let Some(addr) = rs.next() { @@ -72,12 +72,14 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result { return Err(Error::JavaException); } }; - let nat_test_server = nat_test_server.split(",").flat_map(|a| a.trim().to_socket_addrs()).flatten() - .collect::>(); + let mut stun_server = Vec::new(); + stun_server.push("stun1.l.google.com:19302".to_string()); + stun_server.push("stun2.l.google.com:19302".to_string()); + stun_server.push("stun.qq.com:3478".to_string()); let config = Config::new(false, token, device_id, name, server_address, server_address_str, - nat_test_server, vec![], + stun_server, vec![], vec![], password, false, None, false, None, false); match VntUtilSync::new(config) { Ok(vnt_util) => { diff --git a/vnt/Cargo.toml b/vnt/Cargo.toml index fced49d2..2e1abfe3 100644 --- a/vnt/Cargo.toml +++ b/vnt/Cargo.toml @@ -21,6 +21,8 @@ socket2 ={ version = "0.5.2", features = ["all"] } tokio = { version = "1.28.1", features = ["full"] } aes-gcm = {version="0.10.2", optional = true} ring = {version="0.16.20", optional = true} +stun-format = {version="1.0.1",features=["fmt","rfc3489"]} + [target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies] tun = { path = "./rust-tun" } diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs index 1afa69d1..460c3dd8 100644 --- a/vnt/src/core/mod.rs +++ b/vnt/src/core/mod.rs @@ -181,16 +181,16 @@ impl VntUtil { let local_ip = crate::nat::local_ip()?; let local_port = context.main_local_port()?; // NAT检测 - let nat_test = NatTest::new(config.nat_test_server.clone(), response.public_ip, response.public_port, local_ip, local_port); + let nat_test = NatTest::new(config.stun_server.clone(), response.public_ip, response.public_port, local_ip, local_port).await; let in_external_route = if config.in_ips.is_empty() { None } else { Some(ExternalRoute::new(config.in_ips)) }; - let (tcp_proxy, udp_proxy, ip_proxy_map) = if config.out_ips.is_empty() { + let (tcp_proxy, udp_proxy, ip_proxy_map) = if config.out_ips.is_empty() { (None, None, None) } else { - let (tcp_proxy, udp_proxy, ip_proxy_map) = crate::ip_proxy::init_proxy(channel_sender.clone(), current_device.clone()).await?; + let (tcp_proxy, udp_proxy, ip_proxy_map) = crate::ip_proxy::init_proxy(channel_sender.clone(), current_device.clone()).await?; (Some(tcp_proxy), Some(udp_proxy), Some(ip_proxy_map)) }; let out_external_route = AllowExternalRoute::new(config.out_ips); @@ -335,7 +335,7 @@ pub struct Config { pub name: String, pub server_address: SocketAddr, pub server_address_str: String, - pub nat_test_server: Vec, + pub stun_server: Vec, pub in_ips: Vec<(u32, u32, Ipv4Addr)>, pub out_ips: Vec<(u32, u32)>, pub password: Option, @@ -353,11 +353,16 @@ impl Config { name: String, server_address: SocketAddr, server_address_str: String, - nat_test_server: Vec, + mut stun_server: Vec, in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32)>, password: Option, simulate_multicast: bool, mtu: Option, tcp: bool, ip: Option, relay: bool, ) -> Self { + for x in stun_server.iter_mut() { + if !x.contains(":") { + x.push_str(":3478"); + } + } Self { tap, token, @@ -365,7 +370,7 @@ impl Config { name, server_address, server_address_str, - nat_test_server, + stun_server, in_ips, out_ips, password, diff --git a/vnt/src/handle/recv_handler.rs b/vnt/src/handle/recv_handler.rs index afbe2a88..7ea44ddb 100644 --- a/vnt/src/handle/recv_handler.rs +++ b/vnt/src/handle/recv_handler.rs @@ -270,7 +270,7 @@ impl ChannelDataHandler { let local_port = context.main_local_port()?; let local_ip = nat::local_ip()?; let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), - response.public_port as u16, local_ip, local_port); + response.public_port as u16, local_ip, local_port).await; context.switch(nat_info.nat_type); let new_ip = Ipv4Addr::from(response.virtual_ip); let current_ip = current_device.virtual_ip(); diff --git a/vnt/src/nat/check.rs b/vnt/src/nat/check.rs deleted file mode 100644 index 155a3628..00000000 --- a/vnt/src/nat/check.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::collections::HashSet; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; -use std::time::Duration; -use std::{io, thread}; -use crate::channel::punch::NatType; - - -/// 返回所有公网ip和端口变化范围 -pub fn public_ip_list(addrs: &Vec) -> io::Result<(NatType, Vec, u16)> { - let mut hash_set = HashSet::new(); - let mut max_port_range = 0; - let mut nat_type = NatType::Cone; - let mut port = 88; - for _ in 0..3 { - let udp = loop { - match UdpSocket::bind(SocketAddr::new(IpAddr::from(Ipv4Addr::from(0)), port)) { - Ok(udp) => { - break udp; - } - Err(e) => { - if e.kind() == io::ErrorKind::AddrInUse { - port += 1; - continue; - } - return Err(e); - } - } - }; - let (set, min_port, max_port) = public_ip_list_(&udp, addrs)?; - drop(udp); - let port_range = max_port - min_port; - //有多个ip或者端口有变化,说明是对称nat - if nat_type == NatType::Cone && (set.len() > 1 || port_range != 0) { - nat_type = NatType::Symmetric; - } - if max_port_range < port_range { - max_port_range = port_range; - } - for x in set { - hash_set.insert(x); - } - thread::sleep(Duration::from_micros(5)); - } - Ok((nat_type, hash_set.into_iter().collect(), max_port_range)) -} - -/// 测试样本较少,可能不对 -/// -/// - 移动宽带:锥形网络、一个ip、端口和局域网端口不相同 -/// - 电信宽带:锥形网络、一个ip,端口和局域网端口不相同 -/// - 联调宽带:对称网络、端口不变ip轮流用 -/// - 移动4g:对称网络、ip端口都变 使用小的端口变化量小 -/// - 联通4g:对称网络、只有一个ip 端口变化大 -/// - 电信4g:对称网络只有一个ip 公网端口比较连续 -/// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围 -/// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g -pub fn public_ip_list_( - udp: &UdpSocket, - addrs: &Vec, -) -> io::Result<(HashSet, u16, u16)> { - udp.set_read_timeout(Some(Duration::from_millis(300)))?; - let mut buf = [0u8; 128]; - for addr in addrs { - let _ = udp.send_to(b"NatTest", addr)?; - } - let mut hash_set = HashSet::new(); - let mut count = 0; - let mut min_port = 65535; - let mut max_port = 0; - for _ in 0..addrs.len() { - if let Ok(len) = udp.recv(&mut buf) { - if len != 16 || &buf[..10] != &b"NatType213"[..] { - continue; - } - let port = u16::from_be_bytes([buf[14], buf[15]]); - if min_port > port { - min_port = port; - } - if max_port < port { - max_port = port; - } - let ip = Ipv4Addr::new(buf[10], buf[11], buf[12], buf[13]); - hash_set.insert(ip); - count += 1; - } - } - if count <= 1 { - return Err(io::Error::from(io::ErrorKind::TimedOut)); - } - Ok((hash_set, min_port, max_port)) -} - -/// 返回nat类型 -pub fn nat_test() -> io::Result { - for _ in 0..3 { - if NatType::Symmetric == nat_test_()? { - return Ok(NatType::Symmetric); - } - thread::sleep(Duration::from_micros(5)); - } - Ok(NatType::Cone) -} - -pub fn nat_test_() -> io::Result { - let udp = UdpSocket::bind("0.0.0.0:0")?; - udp.set_read_timeout(Some(Duration::from_millis(300)))?; - let mut buf = [0u8; 128]; - let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?; - let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?; - let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?; - let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?; - let mut tmp_ip_port: Option<[u8; 6]> = None; - let mut count = 0; - for _ in 0..4 { - if let Ok(len) = udp.recv(&mut buf) { - if len != 16 || &buf[..10] != &b"NatType213"[..] { - continue; - } - count += 1; - let mut ip_port = [0u8; 6]; - ip_port.copy_from_slice(&buf[10..16]); - if let Some(tmp_ip_port) = &tmp_ip_port { - if tmp_ip_port != &ip_port { - return Ok(NatType::Symmetric); - } - } else { - tmp_ip_port = Some(ip_port); - } - } - } - if count <= 1 { - return Err(io::Error::from(io::ErrorKind::TimedOut)); - } - Ok(NatType::Cone) -} - -#[test] -fn nat_test_run() { - let udp = UdpSocket::bind("0.0.0.0:101").unwrap(); - use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}; - let addrs = vec![ - "nat1.wherewego.top:35062" - .to_socket_addrs() - .unwrap() - .next() - .unwrap(), - "nat2.wherewego.top:35062" - .to_socket_addrs() - .unwrap() - .next() - .unwrap(), - ]; - let print = public_ip_list_(&udp, &addrs).unwrap(); - println!("{:?}", print); -} diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs index 10c3b19f..e47a3483 100644 --- a/vnt/src/nat/mod.rs +++ b/vnt/src/nat/mod.rs @@ -1,13 +1,14 @@ -use crate::proto::message::PunchNatType; -use parking_lot::Mutex; use std::io; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv4Addr}; +use std::net::UdpSocket; use std::sync::Arc; -pub mod check; +use parking_lot::Mutex; -use std::net::UdpSocket; use crate::channel::punch::{NatInfo, NatType}; +use crate::proto::message::PunchNatType; + +mod stun_test; pub fn local_ip() -> io::Result { let socket = UdpSocket::bind("0.0.0.0:0")?; @@ -25,7 +26,7 @@ pub fn local_ip() -> io::Result { #[derive(Clone)] pub struct NatTest { - nat_test_server: Arc>, + stun_server: Vec, info: Arc>, } @@ -48,22 +49,24 @@ impl Into for PunchNatType { } impl NatTest { - pub fn new( - nat_test_server: Vec, + pub async fn new( + mut stun_server: Vec, public_ip: Ipv4Addr, public_port: u16, local_ip: Ipv4Addr, local_port: u16, ) -> NatTest { + let server = stun_server[0].clone(); + stun_server.resize(3, server); let info = NatTest::re_test_( - &nat_test_server, + &stun_server, public_ip, public_port, local_ip, local_port, - ); + ).await; NatTest { - nat_test_server: Arc::new(nat_test_server), + stun_server, info: Arc::new(Mutex::new(info)), } } @@ -77,7 +80,7 @@ impl NatTest { guard.public_ips.push(ip); } } - pub fn re_test( + pub async fn re_test( &self, public_ip: Ipv4Addr, public_port: u16, @@ -85,23 +88,23 @@ impl NatTest { local_port: u16, ) -> NatInfo { let info = NatTest::re_test_( - &self.nat_test_server, + &self.stun_server, public_ip, public_port, local_ip, local_port, - ); + ).await; *self.info.lock() = info.clone(); info } - fn re_test_( - nat_test_server: &Vec, + async fn re_test_( + stun_server: &Vec, public_ip: Ipv4Addr, public_port: u16, local_ip: Ipv4Addr, local_port: u16, ) -> NatInfo { - return match check::public_ip_list(nat_test_server) { + return match stun_test::stun_test_nat(stun_server.clone()).await { Ok((nat_type, ips, port_range)) => { let mut public_ips = Vec::new(); public_ips.push(Ipv4Addr::from(public_ip)); diff --git a/vnt/src/nat/stun_test.rs b/vnt/src/nat/stun_test.rs new file mode 100644 index 00000000..670452d7 --- /dev/null +++ b/vnt/src/nat/stun_test.rs @@ -0,0 +1,129 @@ +use std::collections::HashSet; +use std::io; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::time::Duration; + +use stun_format::Attr; +use tokio::net::UdpSocket; +use crate::channel::punch::NatType; + +pub async fn stun_test_nat(stun_servers: Vec) -> io::Result<(NatType, Vec, u16)> { + let mut h = Vec::new(); + for x in stun_servers { + let handle = tokio::spawn(test_nat(x)); + h.push(handle); + } + let mut nat_type = NatType::Cone; + let mut port_range = 0; + let mut hash_set = HashSet::new(); + for x in h { + if let Ok(rs) = x.await { + if let Ok((nat_type_t, ip_list_t, port_range_t)) = rs { + if nat_type_t == NatType::Symmetric { + nat_type = NatType::Symmetric; + } + for x in ip_list_t { + hash_set.insert(x); + } + if port_range < port_range_t { + port_range = port_range_t; + } + } + } + } + Ok((nat_type, hash_set.into_iter().collect(), port_range)) +} + +async fn test_nat(stun_server: String) -> io::Result<(NatType, Vec, u16)> { + let udp = UdpSocket::bind("0.0.0.0:0").await?; + udp.connect(stun_server).await?; + let mut nat_type = NatType::Cone; + let mut port_range = 0; + let mut hash_set = HashSet::new(); + match test_nat_(&udp, true, true).await { + Ok((mapped_addr1, changed_addr1)) => { + match mapped_addr1.ip() { + IpAddr::V4(ip) => { + hash_set.insert(ip); + } + IpAddr::V6(_) => {} + } + if udp.connect(changed_addr1).await.is_ok() { + if let Ok((mapped_addr2, _)) = test_nat_(&udp, false, false).await { + match mapped_addr2.ip() { + IpAddr::V4(ip) => { + hash_set.insert(ip); + } + IpAddr::V6(_) => {} + } + port_range = mapped_addr2.port().abs_diff(mapped_addr1.port()); + if mapped_addr1 != mapped_addr2 { + nat_type = NatType::Symmetric; + } + } + } + } + Err(_) => {} + } + Ok((nat_type, hash_set.into_iter().collect(), port_range)) +} + +async fn test_nat_(udp: &UdpSocket, change_ip: bool, change_port: bool) -> io::Result<(SocketAddr, SocketAddr)> { + 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(1).unwrap(); + msg.add_attr(Attr::ChangeRequest { change_ip, change_port }).unwrap(); + udp.send(msg.as_bytes()).await?; + let mut buf = [0; 10240]; + let (len, addr) = match tokio::time::timeout(Duration::from_millis(300), udp.recv_from(&mut buf)).await { + Ok(rs) => { rs? } + Err(_) => { + continue; + } + }; + let msg = stun_format::Msg::from(&buf[..len]); + let mut mapped_addr = None; + let mut changed_addr = None; + for x in msg.attrs_iter() { + match x { + Attr::MappedAddress(addr) => { + if mapped_addr.is_none() { + let _ = mapped_addr.insert(stun_addr(addr)); + } + } + Attr::ChangedAddress(addr) => { + if changed_addr.is_none() { + let _ = changed_addr.insert(stun_addr(addr)); + } + } + Attr::XorMappedAddress(addr) => { + if mapped_addr.is_none() { + let _ = mapped_addr.insert(stun_addr(addr)); + } + } + _ => {} + } + if changed_addr.is_some() && mapped_addr.is_some() { + return Ok((mapped_addr.unwrap(), changed_addr.unwrap())); + } + } + if mapped_addr.is_some() { + return Ok((mapped_addr.unwrap(), changed_addr.unwrap_or(addr))); + } + } + Err(io::Error::new(io::ErrorKind::Other, "stun response err")) +} + +fn stun_addr(addr: stun_format::SocketAddr) -> SocketAddr { + match addr { + stun_format::SocketAddr::V4(ip, port) => { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(ip), port)) + } + stun_format::SocketAddr::V6(ip, port) => { + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(ip), port, 0, 0)) + } + } +} +