Skip to content

Commit

Permalink
Fix dns v6 (#109)
Browse files Browse the repository at this point in the history
* fix dns v6

* impl exchange
  • Loading branch information
ibigbug authored Oct 5, 2023
1 parent 61bfff2 commit d806457
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 45 deletions.
9 changes: 6 additions & 3 deletions clash/tests/data/config/rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clash_lib/src/app/dns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub trait ClashResolver: Sync + Send {
enhanced: bool,
) -> anyhow::Result<Option<std::net::Ipv6Addr>>;

async fn exchange(&self, message: op::Message) -> anyhow::Result<op::Message>;

/// Only used for look up fake IP
async fn reverse_lookup(&self, ip: std::net::IpAddr) -> Option<String>;
async fn is_fake_ip(&self, ip: std::net::IpAddr) -> bool;
Expand Down
30 changes: 26 additions & 4 deletions clash_lib/src/app/dns/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -490,6 +507,10 @@ impl ClashResolver for Resolver {
}
}

async fn exchange(&self, message: op::Message) -> anyhow::Result<op::Message> {
self.exchange(message).await
}

fn ipv6(&self) -> bool {
self.ipv6.load(Relaxed)
}
Expand All @@ -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;
Expand Down
80 changes: 42 additions & 38 deletions clash_lib/src/app/dns/server/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use std::{net::IpAddr, time::Duration};
use std::time::Duration;

use async_trait::async_trait;

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,
Expand Down Expand Up @@ -40,8 +37,6 @@ pub enum DNSError {
QueryFailed(String),
}

static DEFAULT_DNS_SERVER_TTL: u32 = 60;

impl DnsHandler {
async fn handle<R: ResponseHandler>(
&self,
Expand All @@ -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()))
Expand Down
7 changes: 7 additions & 0 deletions clash_lib/src/app/dns/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ impl ClashResolver for SystemResolver {
.choose(&mut rand::thread_rng()))
}

async fn exchange(
&self,
_: trust_dns_proto::op::Message,
) -> anyhow::Result<trust_dns_proto::op::Message> {
Err(anyhow::anyhow!("unsupported"))
}

fn ipv6(&self) -> bool {
true
}
Expand Down

0 comments on commit d806457

Please sign in to comment.