diff --git a/clash/tests/data/config/rules.yaml b/clash/tests/data/config/rules.yaml index b0ce1a4a6..c7d9eb5c7 100644 --- a/clash/tests/data/config/rules.yaml +++ b/clash/tests/data/config/rules.yaml @@ -4,11 +4,14 @@ socks-port: 8889 mixed-port: 8899 tun: - enable: true + enable: false device-id: "dev://utun1989" +ipv6: true + dns: enable: true + ipv6: true listen: 127.0.0.1:53553 # udp: 127.0.0.1:53553 # tcp: 127.0.0.1:53553 @@ -22,7 +25,7 @@ dns: default-nameserver: - 114.114.114.114 - 8.8.8.8 - enhanced-mode: fake-ip + enhanced-mode: normal fake-ip-range: 198.18.0.2/16 # Fake IP addresses pool CIDR # use-hosts: true # lookup hosts and return IP record @@ -215,7 +218,7 @@ rule-providers: rules: - DOMAIN,ipinfo.io,relay - - RULE-SET,file-provider,trojan +# - RULE-SET,file-provider,trojan - GEOIP,CN,relay - DOMAIN-SUFFIX,facebook.com,REJECT - DOMAIN-KEYWORD,google,select diff --git a/clash_lib/src/app/dns/mod.rs b/clash_lib/src/app/dns/mod.rs index 01fec633c..12eb05b39 100644 --- a/clash_lib/src/app/dns/mod.rs +++ b/clash_lib/src/app/dns/mod.rs @@ -83,6 +83,8 @@ pub trait ClashResolver: Sync + Send { enhanced: bool, ) -> anyhow::Result>; + async fn exchange(&self, message: op::Message) -> anyhow::Result; + /// Only used for look up fake IP async fn reverse_lookup(&self, ip: std::net::IpAddr) -> Option; async fn is_fake_ip(&self, ip: std::net::IpAddr) -> bool; diff --git a/clash_lib/src/app/dns/resolver.rs b/clash_lib/src/app/dns/resolver.rs index f7fccda68..6c5416f21 100644 --- a/clash_lib/src/app/dns/resolver.rs +++ b/clash_lib/src/app/dns/resolver.rs @@ -281,11 +281,23 @@ impl Resolver { // TODO: make this TTL wired to LRU cache #[allow(unused_variables)] let ttl = if msg.answer_count() != 0 { - msg.answers().iter().map(|x| x.ttl()).min().unwrap() + msg.answers() + .iter() + .map(|x| x.ttl()) + .min() + .unwrap_or_default() } else if msg.name_server_count() != 0 { - msg.name_servers().iter().map(|x| x.ttl()).min().unwrap() + msg.name_servers() + .iter() + .map(|x| x.ttl()) + .min() + .unwrap_or_default() } else { - msg.additionals().iter().map(|x| x.ttl()).min().unwrap() + msg.additionals() + .iter() + .map(|x| x.ttl()) + .min() + .unwrap_or_default() }; lru.write().await.insert(q.to_string(), msg.clone()); @@ -407,7 +419,12 @@ impl ClashResolver for Resolver { .map(|x| x.map(|v4| v4.map(|v4| net::IpAddr::from(v4)))); let futs = vec![fut1.boxed(), fut2.boxed()]; - futures::future::select_ok(futs).await.map(|v| v.0) + let r = futures::future::select_ok(futs).await?; + if r.0.is_some() { + return Ok(r.0); + } + let r = futures::future::select_all(r.1).await; + return r.0; } false => self .resolve_v4(host, enhanced) @@ -490,6 +507,10 @@ impl ClashResolver for Resolver { } } + async fn exchange(&self, message: op::Message) -> anyhow::Result { + self.exchange(message).await + } + fn ipv6(&self) -> bool { self.ipv6.load(Relaxed) } @@ -505,6 +526,7 @@ impl ClashResolver for Resolver { fn fake_ip_enabled(&self) -> bool { self.fake_dns.is_some() } + async fn is_fake_ip(&self, ip: std::net::IpAddr) -> bool { if !self.fake_ip_enabled() { return false; diff --git a/clash_lib/src/app/dns/server/mod.rs b/clash_lib/src/app/dns/server/mod.rs index 4549e45d0..2d5d38a41 100644 --- a/clash_lib/src/app/dns/server/mod.rs +++ b/clash_lib/src/app/dns/server/mod.rs @@ -1,4 +1,4 @@ -use std::{net::IpAddr, time::Duration}; +use std::time::Duration; use async_trait::async_trait; @@ -6,11 +6,8 @@ use thiserror::Error; use tokio::net::{TcpListener, UdpSocket}; use tracing::{debug, info, warn}; use trust_dns_proto::{ - op::{Header, MessageType, OpCode, ResponseCode}, - rr::{ - rdata::{A, AAAA}, - RData, Record, - }, + op::{Header, Message, MessageType, OpCode, ResponseCode}, + rr::RecordType, }; use trust_dns_server::{ authority::MessageResponseBuilder, @@ -40,8 +37,6 @@ pub enum DNSError { QueryFailed(String), } -static DEFAULT_DNS_SERVER_TTL: u32 = 60; - impl DnsHandler { async fn handle( &self, @@ -62,39 +57,48 @@ impl DnsHandler { ))); } - let name = request.query().name(); - let host = if name.is_fqdn() { - name.to_string().strip_suffix(".").unwrap().to_string() - } else { - name.to_string() - }; - let builder = MessageResponseBuilder::from_message_request(request); let mut header = Header::response_from_request(request.header()); - header.set_authoritative(true); - - match self.resolver.resolve(&host, true).await { - Ok(resp) => match resp { - Some(ip) => { - let rdata = match ip { - IpAddr::V4(a) => RData::A(A(a)), - IpAddr::V6(aaaa) => RData::AAAA(AAAA(aaaa)), - }; - - let records = vec![Record::from_rdata( - name.into(), - DEFAULT_DNS_SERVER_TTL, - rdata, - )]; - - let resp = builder.build(header, records.iter(), &[], &[], &[]); - Ok(response_handle.send_response(resp).await?) - } - None => { - let resp = builder.build_no_records(header); - Ok(response_handle.send_response(resp).await?) + + if request.query().query_type() == RecordType::AAAA && !self.resolver.ipv6() { + header.set_authoritative(true); + + let resp = builder.build_no_records(header); + return Ok(response_handle.send_response(resp).await?); + } + + let mut m = Message::new(); + m.set_op_code(request.op_code()); + m.set_message_type(request.message_type()); + m.add_query(request.query().original().clone()); + m.add_additionals(request.additionals().into_iter().map(Clone::clone)); + m.add_name_servers(request.name_servers().into_iter().map(Clone::clone)); + for sig0 in request.sig0() { + m.add_sig0(sig0.clone()); + } + if let Some(edns) = request.edns() { + m.set_edns(edns.clone()); + } + + match self.resolver.exchange(m).await { + Ok(m) => { + header.set_recursion_available(m.recursion_available()); + header.set_response_code(m.response_code()); + header.set_authoritative(m.authoritative()); + + header.set_answer_count(m.answer_count()); + header.set_name_server_count(m.name_server_count()); + header.set_additional_count(m.additional_count()); + + let mut rv = + builder.build(header, m.answers(), m.name_servers(), &[], m.additionals()); + + if let Some(edns) = m.extensions() { + rv.set_edns(edns.clone()); } - }, + + Ok(response_handle.send_response(rv).await?) + } Err(e) => { debug!("dns resolve error: {}", e); Err(DNSError::QueryFailed(e.to_string())) diff --git a/clash_lib/src/app/dns/system.rs b/clash_lib/src/app/dns/system.rs index bf3874bd4..a7b742bd5 100644 --- a/clash_lib/src/app/dns/system.rs +++ b/clash_lib/src/app/dns/system.rs @@ -51,6 +51,13 @@ impl ClashResolver for SystemResolver { .choose(&mut rand::thread_rng())) } + async fn exchange( + &self, + _: trust_dns_proto::op::Message, + ) -> anyhow::Result { + Err(anyhow::anyhow!("unsupported")) + } + fn ipv6(&self) -> bool { true }