Skip to content

Commit 1ac8e2f

Browse files
committed
Bypasses local DNS queries including server domain names
- ref shadowsocks/shadowsocks-android#2301
1 parent dd830c7 commit 1ac8e2f

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//! Replacement of service's DNS resolver
2+
3+
use std::{
4+
io::{self, ErrorKind},
5+
net::SocketAddr,
6+
};
7+
8+
use async_trait::async_trait;
9+
use futures::future;
10+
use log::{debug, trace};
11+
use shadowsocks::{dns_resolver::DnsResolve, net::ConnectOpts};
12+
use trust_dns_resolver::proto::{
13+
op::{Message, Query},
14+
rr::{DNSClass, Name, RData, RecordType},
15+
};
16+
17+
use crate::config::Mode;
18+
19+
use super::{client_cache::DnsClientCache, config::NameServerAddr};
20+
21+
pub struct DnsResolver {
22+
ns: NameServerAddr,
23+
client_cache: DnsClientCache,
24+
mode: Mode,
25+
ipv6_first: bool,
26+
connect_opts: ConnectOpts,
27+
attempts: usize,
28+
}
29+
30+
impl DnsResolver {
31+
pub fn new(ns: NameServerAddr) -> DnsResolver {
32+
DnsResolver {
33+
ns,
34+
client_cache: DnsClientCache::new(5),
35+
mode: Mode::UdpOnly,
36+
ipv6_first: false,
37+
connect_opts: ConnectOpts::default(),
38+
attempts: 2,
39+
}
40+
}
41+
42+
pub fn set_mode(&mut self, mode: Mode) {
43+
self.mode = mode;
44+
}
45+
46+
pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
47+
self.ipv6_first = ipv6_first;
48+
}
49+
50+
pub fn set_connect_opts(&mut self, connect_opts: ConnectOpts) {
51+
self.connect_opts = connect_opts;
52+
}
53+
54+
async fn lookup(&self, msg: Message) -> io::Result<Message> {
55+
let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty");
56+
57+
for _ in 0..self.attempts {
58+
match self.lookup_inner(msg.clone()).await {
59+
Ok(m) => return Ok(m),
60+
Err(err) => last_err = err,
61+
}
62+
}
63+
64+
Err(last_err)
65+
}
66+
67+
async fn lookup_inner(&self, msg: Message) -> io::Result<Message> {
68+
match self.ns {
69+
NameServerAddr::SocketAddr(ns) => {
70+
let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty");
71+
72+
// Query UDP then TCP
73+
74+
if self.mode.enable_udp() {
75+
match self
76+
.client_cache
77+
.lookup_udp_local(ns, msg.clone(), &self.connect_opts)
78+
.await
79+
{
80+
Ok(msg) => return Ok(msg),
81+
Err(err) => {
82+
last_err = err.into();
83+
}
84+
}
85+
}
86+
87+
if self.mode.enable_tcp() {
88+
match self.client_cache.lookup_tcp_local(ns, msg, &self.connect_opts).await {
89+
Ok(msg) => return Ok(msg),
90+
Err(err) => {
91+
last_err = err.into();
92+
}
93+
}
94+
}
95+
96+
Err(last_err)
97+
}
98+
99+
#[cfg(unix)]
100+
NameServerAddr::UnixSocketAddr(ref path) => self
101+
.client_cache
102+
.lookup_unix_stream(path, msg)
103+
.await
104+
.map_err(From::from),
105+
}
106+
}
107+
}
108+
109+
#[async_trait]
110+
impl DnsResolve for DnsResolver {
111+
async fn resolve(&self, host: &str, port: u16) -> io::Result<Vec<SocketAddr>> {
112+
let mut name = Name::from_utf8(host)?;
113+
name.set_fqdn(true);
114+
115+
let mut queryv4 = Query::new();
116+
queryv4.set_query_class(DNSClass::IN);
117+
queryv4.set_name(name);
118+
119+
let mut queryv6 = queryv4.clone();
120+
queryv4.set_query_type(RecordType::A);
121+
queryv6.set_query_type(RecordType::AAAA);
122+
123+
let mut msgv4 = Message::new();
124+
msgv4.set_recursion_desired(true);
125+
msgv4.add_query(queryv4);
126+
127+
let mut msgv6 = Message::new();
128+
msgv6.set_recursion_desired(true);
129+
msgv6.add_query(queryv6);
130+
131+
let (res_v4, res_v6) = future::join(self.lookup(msgv4), self.lookup(msgv6)).await;
132+
133+
if res_v4.is_err() && res_v6.is_err() {
134+
return if self.ipv6_first {
135+
Err(res_v6.unwrap_err())
136+
} else {
137+
Err(res_v4.unwrap_err())
138+
};
139+
}
140+
141+
let mut vaddr = Vec::new();
142+
143+
if self.ipv6_first {
144+
match res_v6 {
145+
Ok(res) => {
146+
for record in res.answers() {
147+
match *record.rdata() {
148+
RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
149+
RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
150+
ref rdata => {
151+
trace!("skipped rdata {:?}", rdata);
152+
}
153+
}
154+
}
155+
}
156+
Err(err) => {
157+
debug!("failed to resolve AAAA records, error: {}", err);
158+
}
159+
}
160+
161+
match res_v4 {
162+
Ok(res) => {
163+
for record in res.answers() {
164+
match *record.rdata() {
165+
RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
166+
RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
167+
ref rdata => {
168+
trace!("skipped rdata {:?}", rdata);
169+
}
170+
}
171+
}
172+
}
173+
Err(err) => {
174+
debug!("failed to resolve A records, error: {}", err);
175+
}
176+
}
177+
} else {
178+
match res_v4 {
179+
Ok(res) => {
180+
for record in res.answers() {
181+
match *record.rdata() {
182+
RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
183+
RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
184+
ref rdata => {
185+
trace!("skipped rdata {:?}", rdata);
186+
}
187+
}
188+
}
189+
}
190+
Err(err) => {
191+
debug!("failed to resolve A records, error: {}", err);
192+
}
193+
}
194+
195+
match res_v6 {
196+
Ok(res) => {
197+
for record in res.answers() {
198+
match *record.rdata() {
199+
RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
200+
RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)),
201+
ref rdata => {
202+
trace!("skipped rdata {:?}", rdata);
203+
}
204+
}
205+
}
206+
}
207+
Err(err) => {
208+
debug!("failed to resolve AAAA records, error: {}", err);
209+
}
210+
}
211+
}
212+
213+
if vaddr.is_empty() {
214+
let err = io::Error::new(ErrorKind::InvalidData, "resolve empty");
215+
return Err(err);
216+
}
217+
218+
Ok(vaddr)
219+
}
220+
}

crates/shadowsocks-service/src/local/dns/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ pub use self::{config::NameServerAddr, server::Dns};
44

55
mod client_cache;
66
pub mod config;
7+
pub mod dns_resolver;
78
pub mod server;
89
mod upstream;

crates/shadowsocks-service/src/local/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,19 @@ pub async fn run(mut config: Config) -> io::Result<()> {
122122
// }
123123
// }
124124

125+
#[cfg(feature = "local-dns")]
126+
if let Some(ref ns) = config.local_dns_addr {
127+
use crate::{config::Mode, local::dns::dns_resolver::DnsResolver as LocalDnsResolver};
128+
129+
trace!("initializing direct DNS resolver for {}", ns);
130+
131+
let mut resolver = LocalDnsResolver::new(ns.clone());
132+
resolver.set_mode(Mode::TcpAndUdp);
133+
resolver.set_ipv6_first(config.ipv6_first);
134+
resolver.set_connect_opts(context.connect_opts_ref().clone());
135+
context.set_dns_resolver(Arc::new(DnsResolver::custom_resolver(resolver)));
136+
}
137+
125138
#[cfg(feature = "trust-dns")]
126139
if context.dns_resolver().is_system_resolver() {
127140
if config.dns.is_some() || crate::hint_support_default_system_resolver() {

0 commit comments

Comments
 (0)