From 12d4fc8e980067527f870916c656d8ecbb3984f5 Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Mon, 22 Apr 2024 19:00:05 +0800 Subject: [PATCH] =?UTF-8?q?dns=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnt/Cargo.toml | 3 + vnt/src/core/mod.rs | 10 +- vnt/src/handle/maintain/idle.rs | 16 ++- vnt/src/handle/mod.rs | 3 + vnt/src/util/dns_query.rs | 228 ++++++++++++++++++++++++++++++++ vnt/src/util/mod.rs | 3 + 6 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 vnt/src/util/dns_query.rs diff --git a/vnt/Cargo.toml b/vnt/Cargo.toml index 32a456f..724d680 100644 --- a/vnt/Cargo.toml +++ b/vnt/Cargo.toml @@ -32,6 +32,9 @@ libsm = {git="https://github.com/lbl8603/libsm" ,optional = true} mio = {version = "0.8.10",features = ["os-poll","net"]} crossbeam-queue = "0.3.11" +anyhow = "1.0.82" +trust-dns-proto = "0.23.2" + [target.'cfg(target_os = "windows")'.dependencies] libloading = "0.8.0" diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs index a297b7e..d20345c 100644 --- a/vnt/src/core/mod.rs +++ b/vnt/src/core/mod.rs @@ -6,6 +6,7 @@ pub use conn::Vnt; use crate::channel::punch::PunchModel; use crate::channel::UseChannelType; use crate::cipher::CipherModel; +use crate::util::RecordPriority; mod conn; @@ -16,8 +17,9 @@ pub struct Config { pub token: String, pub device_id: String, pub name: String, - pub server_address: SocketAddr, pub server_address_str: String, + pub name_servers:Vec, + pub record_priority:RecordPriority, pub stun_server: Vec, pub in_ips: Vec<(u32, u32, Ipv4Addr)>, pub out_ips: Vec<(u32, u32)>, @@ -48,8 +50,9 @@ impl Config { token: String, device_id: String, name: String, - server_address: SocketAddr, server_address_str: String, + name_servers:Vec, + record_priority:RecordPriority, mut stun_server: Vec, in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32)>, @@ -90,8 +93,9 @@ impl Config { token, device_id, name, - server_address, server_address_str, + name_servers, + record_priority, stun_server, in_ips, out_ips, diff --git a/vnt/src/handle/maintain/idle.rs b/vnt/src/handle/maintain/idle.rs index 1c709e9..7ff6558 100644 --- a/vnt/src/handle/maintain/idle.rs +++ b/vnt/src/handle/maintain/idle.rs @@ -12,7 +12,7 @@ use crate::channel::sender::AcceptSocketSender; use crate::handle::callback::{ConnectInfo, ErrorType}; use crate::handle::handshaker::Handshake; use crate::handle::{BaseConfigInfo, ConnectStatus, CurrentDeviceInfo}; -use crate::util::Scheduler; +use crate::util::{dns_query_all, Scheduler}; use crate::{ErrorInfo, VntCallback}; pub fn idle_route( @@ -162,10 +162,18 @@ pub fn domain_request0( config: &BaseConfigInfo, ) -> CurrentDeviceInfo { let mut current_dev = current_device.load(); + // 探测服务端地址变化 - match config.server_addr.to_socket_addrs() { - Ok(mut addr) => { - if let Some(addr) = addr.next() { + match dns_query_all(&config.server_addr,config.name_servers.clone()) { + Ok(mut addrs) => { + for x in &addrs { + //选出一个能用的地址 + if x.is_ipv4(){ + } + todo!() + + } + if let Some(addr) = addrs.pop() { if addr != current_dev.connect_server { let mut tmp = current_dev.clone(); tmp.connect_server = addr; diff --git a/vnt/src/handle/mod.rs b/vnt/src/handle/mod.rs index 3f8b9bf..308f8e8 100644 --- a/vnt/src/handle/mod.rs +++ b/vnt/src/handle/mod.rs @@ -62,6 +62,7 @@ pub struct BaseConfigInfo { pub server_secret: bool, pub device_id: String, pub server_addr: String, + pub name_servers:Vec, } impl BaseConfigInfo { @@ -73,6 +74,7 @@ impl BaseConfigInfo { server_secret: bool, device_id: String, server_addr: String, + name_servers:Vec, ) -> Self { Self { name, @@ -82,6 +84,7 @@ impl BaseConfigInfo { server_secret, device_id, server_addr, + name_servers, } } } diff --git a/vnt/src/util/dns_query.rs b/vnt/src/util/dns_query.rs new file mode 100644 index 0000000..80155f9 --- /dev/null +++ b/vnt/src/util/dns_query.rs @@ -0,0 +1,228 @@ +use std::{io, thread}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket}; +use std::str::FromStr; +use std::time::Duration; + +use anyhow::Context; +use trust_dns_proto::op::{Edns, Message, MessageType, OpCode, Query}; +use trust_dns_proto::rr::{Name, RecordType}; +use trust_dns_proto::xfer::DnsRequestOptions; + +/// 解析记录优先级 +pub enum RecordPriority{ + Ipv4, + Ipv6, +} +impl FromStr for RecordPriority { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + "ipv4" => Ok(RecordPriority::Ipv4), + "ipv6" => Ok(RecordPriority::Ipv6), + _ => Err(format!("not match '{}', enum: ipv4/ipv6", s)), + } + } +} +impl Default for RecordPriority { + fn default() -> Self { + RecordPriority::Ipv6 + } +} +pub fn dns_query_all( + domain: &str, + name_servers: Vec, +) -> anyhow::Result> { + match SocketAddr::from_str(domain) { + Ok(addr) => { + return Ok(vec![addr]); + } + Err(_) => { + if name_servers.is_empty() { + Err(anyhow::anyhow!("name server is none"))? + } + let mut err: Option = None; + for name_server in name_servers { + if let Some(domain) = domain.strip_prefix("txt:") { + return txt_dns(domain, name_server); + } + let end_index = domain + .rfind(":") + .with_context(|| format!("{:?} not port", domain))?; + let host = &domain[..end_index]; + let port = u16::from_str(&domain[end_index + 1..]) + .with_context(|| format!("{:?} not port", domain))?; + let th1 = { + let host = host.to_string(); + thread::spawn(move || a_dns(host, name_server)) + }; + let th2 = { + let host = host.to_string(); + thread::spawn(move || aaaa_dns(host, name_server)) + }; + let mut addr = Vec::new(); + match th1.join().unwrap() { + Ok(rs) => { + for ip in rs { + addr.push(SocketAddr::new(ip.into(), port)); + } + } + Err(e) => { + err.replace(anyhow::anyhow!("{}",e)); + } + } + match th2.join().unwrap() { + Ok(rs) => { + for ip in rs { + addr.push(SocketAddr::new(ip.into(), port)); + } + } + Err(e) => { + if addr.is_empty() { + if let Some(err) = &mut err { + *err = anyhow::anyhow!("{},{}",err,e); + } else { + err.replace(anyhow::anyhow!("{}",e)); + } + continue; + } + } + } + if addr.is_empty() { + continue; + } + return Ok(addr); + } + if let Some(e) = err { + Err(e) + } else { + Err(anyhow::anyhow!("dns query failed")) + } + } + } +} + +pub fn dns_query(domain: &str, name_server: SocketAddr) -> anyhow::Result> { + dns_query_all(domain, vec![name_server]) +} + +fn query( + udp: &UdpSocket, + domain: &str, + name_server: SocketAddr, + record_type: RecordType, +) -> anyhow::Result { + let name = Name::from_str(domain).context("domain error")?; + let query = Query::query(name.clone(), record_type); + let mut options = DnsRequestOptions::default(); + options.use_edns = true; + let request = build_message(query, options); + + let request = request.to_vec()?; + udp.connect(name_server) + .with_context(|| format!("name server {:?} error ", name_server)); + let mut count = 0; + let mut buf = [0; 65536]; + let len = loop { + udp.send(&request)?; + + match udp.recv(&mut buf) { + Ok(len) => { + break len; + } + Err(e) => { + if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock { + count += 1; + if count < 3 { + continue; + } + } + Err(e).with_context(|| format!("name server {:?} recv error ", name_server))? + } + }; + }; + + let message = Message::from_vec(&buf[..len]) + .with_context(|| format!("name server {:?} data error ", name_server))?; + if message.answers().is_empty() { + Err(anyhow::anyhow!("{:?} no {} record", domain, record_type))? + } + Ok(message) +} + +pub fn txt_dns(domain: &str, name_server: SocketAddr) -> anyhow::Result> { + let udp = bind_udp(name_server.is_ipv4())?; + let message = query(&udp, domain, name_server, RecordType::TXT)?; + let mut rs = Vec::new(); + for record in message.answers() { + let txt = record + .data() + .context("data none")? + .as_txt() + .context("record type txt is none")?; + let addr = SocketAddr::from_str(&txt.to_string())?; + rs.push(addr); + } + Ok(rs) +} + +fn bind_udp(is_ipv4: bool) -> io::Result { + let udp = if is_ipv4 { + UdpSocket::bind("0.0.0.0:0")? + } else { + UdpSocket::bind("[::]:0")? + }; + udp.set_read_timeout(Some(Duration::from_millis(800)))?; + Ok(udp) +} + +pub fn a_dns(domain: String, name_server: SocketAddr) -> anyhow::Result> { + let udp = bind_udp(name_server.is_ipv4())?; + let message = query(&udp, &domain, name_server, RecordType::A)?; + let mut rs = Vec::new(); + for record in message.answers() { + let a = record + .data() + .context("data none")? + .as_a() + .context("record type A is none")?; + rs.push(a.0); + } + Ok(rs) +} + +pub fn aaaa_dns(domain: String, name_server: SocketAddr) -> anyhow::Result> { + let udp = bind_udp(name_server.is_ipv4())?; + let message = query(&udp, &domain, name_server, RecordType::AAAA)?; + let mut rs = Vec::new(); + for record in message.answers() { + let a = record + .data() + .context("data none")? + .as_aaaa() + .context("record type AAAA is none")?; + rs.push(a.0); + } + Ok(rs) +} + +pub const MAX_PAYLOAD_LEN: u16 = 1232; + +fn build_message(query: Query, options: DnsRequestOptions) -> Message { + // build the message + let mut message: Message = Message::new(); + message + .add_query(query) + .set_message_type(MessageType::Query) + .set_op_code(OpCode::Query) + .set_recursion_desired(options.recursion_desired); + // Extended dns + if options.use_edns { + message + .extensions_mut() + .get_or_insert_with(Edns::new) + .set_max_payload(MAX_PAYLOAD_LEN) + .set_version(0); + } + message +} diff --git a/vnt/src/util/mod.rs b/vnt/src/util/mod.rs index ce8a1f9..c0f668a 100644 --- a/vnt/src/util/mod.rs +++ b/vnt/src/util/mod.rs @@ -7,3 +7,6 @@ pub use scheduler::Scheduler; mod counter; pub use counter::*; + +mod dns_query; +pub use dns_query::*;