Skip to content

Commit fffc9bf

Browse files
committed
impl: use dhcp assigned dns
1 parent be76202 commit fffc9bf

File tree

7 files changed

+272
-83
lines changed

7 files changed

+272
-83
lines changed

boltconn/src/app.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,11 @@ impl App {
329329
.map_err(|e| anyhow!("Load intercept rules failed: {}", e))?,
330330
);
331331

332-
self.linked_state.lock().unwrap().state = loaded_config.state;
332+
// this is atomic
333+
self.dns.replace_resolvers(&self.outbound_iface, group)?;
334+
335+
// start atomic replacing
333336

334-
self.dns.replace_resolvers(&self.outbound_iface, group);
335337
self.dns.replace_ns_policy(ns_policy);
336338
self.dns.replace_hosts(&config.dns.hosts);
337339
self.dns_hijack_ctrl.update(
@@ -340,7 +342,8 @@ impl App {
340342
SocketAddr::new(Ipv4Addr::new(198, 18, 99, 88).into(), 53),
341343
);
342344

343-
// start atomic replacing
345+
self.linked_state.lock().unwrap().state = loaded_config.state;
346+
344347
self.api_dispatching_handler.store(dispatching.clone());
345348
let hcap2 = self.http_capturer.clone();
346349
self.dispatcher.replace_dispatching(dispatching);
@@ -453,7 +456,7 @@ async fn initialize_dns(
453456
&config.hosts,
454457
ns_policy,
455458
group,
456-
))
459+
)?)
457460
})
458461
}
459462

boltconn/src/network/dns/dns.rs

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::network::dns::dns_table::DnsTable;
33
use crate::network::dns::hosts::HostsResolver;
44
use crate::network::dns::ns_policy::{DispatchedDnsResolver, NameserverPolicies};
55
use crate::network::dns::provider::IfaceProvider;
6-
use crate::proxy::error::TransportError;
6+
use crate::network::dns::{default_resolver_opt, AuxiliaryResolver, NameServerConfigEnum};
7+
use crate::proxy::error::{DnsError, TransportError};
78
use arc_swap::ArcSwap;
89
use hickory_proto::op::{Message, MessageType, ResponseCode};
910
use hickory_proto::rr::{DNSClass, RData, Record, RecordType};
@@ -52,13 +53,40 @@ macro_rules! impl_genuine_lookup {
5253
};
5354
}
5455

56+
macro_rules! apply_resolver {
57+
($func_name:ident, $name:expr, $domain_name:expr,$auxiliary_resolver:expr) => {
58+
match $auxiliary_resolver {
59+
AuxiliaryResolver::Dhcp(inner) => {
60+
let resolver = {
61+
let mut guard = inner.lock().unwrap();
62+
match guard.refresh() {
63+
Ok(_) => {}
64+
Err(e) => {
65+
tracing::warn!(
66+
"failed to update DHCP DNS at ({},{},{}): {:?}",
67+
guard.iface,
68+
guard.iface_addr,
69+
guard.ns_addr,
70+
e
71+
);
72+
}
73+
}
74+
guard.get_resolver()
75+
};
76+
$func_name($name, $domain_name, &resolver).await
77+
}
78+
AuxiliaryResolver::Resolver(inner) => $func_name($name, $domain_name, &inner).await,
79+
}
80+
};
81+
}
82+
5583
pub struct GenericDns<P: RuntimeProvider> {
5684
name: String,
5785
table: DnsTable,
5886
preference: DnsPreference,
5987
host_resolver: ArcSwap<HostsResolver>,
6088
ns_policy: ArcSwap<NameserverPolicies>,
61-
resolvers: ArcSwap<Vec<AsyncResolver<GenericConnector<P>>>>,
89+
resolvers: ArcSwap<Vec<AuxiliaryResolver<AsyncResolver<GenericConnector<P>>>>>,
6290
}
6391

6492
pub type Dns = GenericDns<IfaceProvider>;
@@ -70,28 +98,18 @@ impl Dns {
7098
preference: DnsPreference,
7199
hosts: &HashMap<String, IpAddr>,
72100
ns_policy: NameserverPolicies,
73-
configs: Vec<NameServerConfigGroup>,
74-
) -> Dns {
75-
let resolvers = configs
76-
.into_iter()
77-
.map(|config| {
78-
let cfg = ResolverConfig::from_parts(None, vec![], config);
79-
AsyncResolver::new(
80-
cfg,
81-
Self::default_resolver_opt(),
82-
GenericConnector::new(IfaceProvider::new(iface_name)),
83-
)
84-
})
85-
.collect();
101+
configs: Vec<NameServerConfigEnum>,
102+
) -> Result<Dns, DnsError> {
103+
let resolvers = Self::build_resolvers(iface_name, configs)?;
86104
let host_resolver = HostsResolver::new(hosts);
87-
Dns {
105+
Ok(Dns {
88106
name: name.to_string(),
89107
table: DnsTable::new(),
90108
preference,
91109
host_resolver: ArcSwap::new(Arc::new(host_resolver)),
92110
ns_policy: ArcSwap::new(Arc::new(ns_policy)),
93111
resolvers: ArcSwap::new(Arc::new(resolvers)),
94-
}
112+
})
95113
}
96114

97115
pub fn replace_hosts(&self, hosts: &HashMap<String, IpAddr>) {
@@ -103,26 +121,38 @@ impl Dns {
103121
self.ns_policy.store(Arc::new(ns_policy));
104122
}
105123

106-
pub fn replace_resolvers(&self, iface_name: &str, configs: Vec<NameServerConfigGroup>) {
107-
let resolvers = configs
108-
.into_iter()
109-
.map(|config| {
110-
let cfg = ResolverConfig::from_parts(None, vec![], config);
111-
AsyncResolver::new(
112-
cfg,
113-
Self::default_resolver_opt(),
114-
GenericConnector::new(IfaceProvider::new(iface_name)),
115-
)
116-
})
117-
.collect();
124+
// This function is atomic
125+
pub fn replace_resolvers(
126+
&self,
127+
iface_name: &str,
128+
configs: Vec<NameServerConfigEnum>,
129+
) -> Result<(), DnsError> {
130+
let resolvers = Self::build_resolvers(iface_name, configs)?;
118131
self.resolvers.store(Arc::new(resolvers));
132+
Ok(())
119133
}
120134

121-
fn default_resolver_opt() -> ResolverOpts {
122-
let mut opts = ResolverOpts::default();
123-
opts.timeout = Duration::from_millis(1600);
124-
opts.attempts = 3;
125-
opts
135+
fn build_resolvers(
136+
iface_name: &str,
137+
configs: Vec<NameServerConfigEnum>,
138+
) -> Result<Vec<AuxiliaryResolver<AsyncResolver<GenericConnector<IfaceProvider>>>>, DnsError>
139+
{
140+
let mut resolvers = Vec::new();
141+
for config in configs.into_iter() {
142+
let resolver = match config {
143+
NameServerConfigEnum::Normal(config) => {
144+
let cfg = ResolverConfig::from_parts(None, vec![], config);
145+
AuxiliaryResolver::new_normal(AsyncResolver::new(
146+
cfg,
147+
default_resolver_opt(),
148+
GenericConnector::new(IfaceProvider::new(iface_name)),
149+
))
150+
}
151+
NameServerConfigEnum::Dhcp(dhcp) => AuxiliaryResolver::new_dhcp(&dhcp)?,
152+
};
153+
resolvers.push(resolver);
154+
}
155+
Ok(resolvers)
126156
}
127157
}
128158

@@ -138,7 +168,7 @@ impl<P: RuntimeProvider> GenericDns<P> {
138168
preference,
139169
host_resolver: ArcSwap::new(Arc::new(HostsResolver::empty())),
140170
ns_policy: ArcSwap::new(Arc::new(NameserverPolicies::empty())),
141-
resolvers: ArcSwap::new(Arc::new(vec![resolver])),
171+
resolvers: ArcSwap::new(Arc::new(vec![AuxiliaryResolver::new_normal(resolver)])),
142172
}
143173
}
144174

@@ -161,35 +191,32 @@ impl<P: RuntimeProvider> GenericDns<P> {
161191

162192
async fn genuine_lookup_v4(&self, domain_name: &str) -> Result<Option<IpAddr>, TransportError> {
163193
for r in self.resolvers.load().iter() {
164-
if let Some(ip) = Self::genuine_lookup_one_v4(&self.name, domain_name, r).await? {
194+
if let Some(ip) = apply_resolver!(genuine_lookup_one_v4, &self.name, domain_name, r)? {
165195
return Ok(Some(ip));
166196
}
167197
}
168198
Ok(None)
169199
}
170200
async fn genuine_lookup_v6(&self, domain_name: &str) -> Result<Option<IpAddr>, TransportError> {
171201
for r in self.resolvers.load().iter() {
172-
if let Some(ip) = Self::genuine_lookup_one_v6(&self.name, domain_name, r).await? {
202+
if let Some(ip) = apply_resolver!(genuine_lookup_one_v6, &self.name, domain_name, r)? {
173203
return Ok(Some(ip));
174204
}
175205
}
176206
Ok(None)
177207
}
178208

179-
impl_genuine_lookup!(genuine_lookup_one_v4, ipv4_lookup);
180-
impl_genuine_lookup!(genuine_lookup_one_v6, ipv6_lookup);
181-
182209
async fn one_v4_wrapper(
183210
name: &str,
184211
domain_name: &str,
185212
resolver: &DispatchedDnsResolver,
186213
) -> Result<Option<IpAddr>, TransportError> {
187214
match resolver {
188215
DispatchedDnsResolver::Iface(resolver) => {
189-
Self::genuine_lookup_one_v4(name, domain_name, resolver).await
216+
apply_resolver!(genuine_lookup_one_v4, name, domain_name, resolver)
190217
}
191218
DispatchedDnsResolver::Plain(resolver) => {
192-
Self::genuine_lookup_one_v4(name, domain_name, resolver).await
219+
genuine_lookup_one_v4(name, domain_name, resolver).await
193220
}
194221
}
195222
}
@@ -201,10 +228,10 @@ impl<P: RuntimeProvider> GenericDns<P> {
201228
) -> Result<Option<IpAddr>, TransportError> {
202229
match resolver {
203230
DispatchedDnsResolver::Iface(resolver) => {
204-
Self::genuine_lookup_one_v6(name, domain_name, resolver).await
231+
apply_resolver!(genuine_lookup_one_v6, name, domain_name, resolver)
205232
}
206233
DispatchedDnsResolver::Plain(resolver) => {
207-
Self::genuine_lookup_one_v6(name, domain_name, resolver).await
234+
genuine_lookup_one_v6(name, domain_name, resolver).await
208235
}
209236
}
210237
}
@@ -331,3 +358,6 @@ impl<P: RuntimeProvider> GenericDns<P> {
331358
}
332359
}
333360
}
361+
362+
impl_genuine_lookup!(genuine_lookup_one_v4, ipv4_lookup);
363+
impl_genuine_lookup!(genuine_lookup_one_v6, ipv6_lookup);

boltconn/src/network/dns/mod.rs

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod ns_policy;
88
mod provider;
99

1010
use crate::config::DnsConfigError;
11-
use crate::proxy::error::DnsError;
11+
use crate::proxy::error::{DnsError, TransportError};
1212
pub use bootstrap::BootstrapResolver;
1313
pub use dns::{Dns, GenericDns};
1414
use hickory_resolver::config::{
@@ -20,6 +20,13 @@ pub use hijack_ctrl::DnsHijackController;
2020
pub use ns_policy::NameserverPolicies;
2121
use provider::IfaceProvider;
2222
use std::net::{IpAddr, SocketAddr};
23+
use std::sync::{Arc, Mutex};
24+
use std::time::Duration;
25+
26+
pub enum NameServerConfigEnum {
27+
Normal(NameServerConfigGroup),
28+
Dhcp(String),
29+
}
2330

2431
fn add_tls_server(
2532
ips: &[IpAddr],
@@ -72,7 +79,7 @@ pub fn new_bootstrap_resolver(iface_name: &str, addr: &[IpAddr]) -> BootstrapRes
7279
pub async fn parse_dns_config(
7380
lines: impl Iterator<Item = &String>,
7481
bootstrap: &BootstrapResolver,
75-
) -> Result<Vec<NameServerConfigGroup>, DnsConfigError> {
82+
) -> Result<Vec<NameServerConfigEnum>, DnsConfigError> {
7683
let mut arr = Vec::new();
7784
for l in lines {
7885
let parts: Vec<&str> = l.split(',').map(|s| s.trim()).collect();
@@ -92,8 +99,11 @@ pub async fn parse_single_dns(
9299
proto: &str,
93100
content: &str,
94101
bootstrap: &BootstrapResolver,
95-
) -> Result<NameServerConfigGroup, DnsConfigError> {
96-
Ok(match proto {
102+
) -> Result<NameServerConfigEnum, DnsConfigError> {
103+
Ok(NameServerConfigEnum::Normal(match proto {
104+
"dhcp" => {
105+
return Ok(NameServerConfigEnum::Dhcp(content.to_string()));
106+
}
97107
"udp" => NameServerConfigGroup::from(vec![NameServerConfig::new(
98108
SocketAddr::new(
99109
content
@@ -127,7 +137,7 @@ pub async fn parse_single_dns(
127137
_ => return Err(DnsConfigError::InvalidPreset("doh", content.to_string())),
128138
},
129139
_ => return Err(DnsConfigError::InvalidType(proto.to_string())),
130-
})
140+
}))
131141
}
132142

133143
pub fn extract_address(group: &[NameServerConfigGroup]) -> Vec<IpAddr> {
@@ -140,3 +150,99 @@ pub fn extract_address(group: &[NameServerConfigGroup]) -> Vec<IpAddr> {
140150
})
141151
.collect()
142152
}
153+
154+
fn default_resolver_opt() -> ResolverOpts {
155+
let mut opts = ResolverOpts::default();
156+
opts.timeout = Duration::from_millis(1600);
157+
opts.attempts = 3;
158+
opts
159+
}
160+
161+
struct DhcpDnsRecord {
162+
iface: String,
163+
iface_addr: IpAddr,
164+
ns_addr: IpAddr,
165+
last_checked: std::time::Instant,
166+
resolver: Arc<AsyncResolver<GenericConnector<IfaceProvider>>>,
167+
}
168+
169+
impl DhcpDnsRecord {
170+
pub fn new(iface: &str) -> Result<Self, DnsError> {
171+
let iface_addr = crate::platform::get_iface_address(iface)
172+
.map_err(|_| DnsError::DhcpNameServer("failed to get iface address"))?;
173+
let ns_addr = crate::platform::dhcp::get_dhcp_dns(iface)?;
174+
tracing::debug!(
175+
"DHCP DNS: iface={}, iface_addr={}, ns_addr={}",
176+
iface,
177+
iface_addr,
178+
ns_addr
179+
);
180+
Ok(Self {
181+
iface: iface.to_string(),
182+
iface_addr,
183+
ns_addr,
184+
last_checked: std::time::Instant::now(),
185+
resolver: Self::create_resolver(ns_addr, iface),
186+
})
187+
}
188+
189+
// return if the record is updated
190+
pub fn refresh(&mut self) -> Result<bool, TransportError> {
191+
if self.last_checked.elapsed() < Duration::from_secs(30) {
192+
Ok(false)
193+
} else {
194+
// when error occurs, update the record in a best-effort way
195+
let addr = crate::platform::get_iface_address(&self.iface)?;
196+
if addr != self.iface_addr {
197+
let new_dns = crate::platform::dhcp::get_dhcp_dns(&self.iface)?;
198+
self.iface_addr = addr;
199+
self.ns_addr = new_dns;
200+
self.last_checked = std::time::Instant::now();
201+
self.resolver = Self::create_resolver(new_dns, &self.iface);
202+
Ok(true)
203+
} else {
204+
self.last_checked = std::time::Instant::now();
205+
Ok(false)
206+
}
207+
}
208+
}
209+
210+
fn create_resolver(
211+
new_dns: IpAddr,
212+
iface: &str,
213+
) -> Arc<AsyncResolver<GenericConnector<IfaceProvider>>> {
214+
let cfg = ResolverConfig::from_parts(
215+
None,
216+
vec![],
217+
NameServerConfigGroup::from(vec![NameServerConfig::new(
218+
SocketAddr::new(new_dns, 53),
219+
Protocol::Udp,
220+
)]),
221+
);
222+
Arc::new(AsyncResolver::new(
223+
cfg,
224+
default_resolver_opt(),
225+
GenericConnector::new(IfaceProvider::new(iface)),
226+
))
227+
}
228+
229+
pub fn get_resolver(&self) -> Arc<AsyncResolver<GenericConnector<IfaceProvider>>> {
230+
self.resolver.clone()
231+
}
232+
}
233+
234+
enum AuxiliaryResolver<T> {
235+
Resolver(T),
236+
Dhcp(Mutex<DhcpDnsRecord>),
237+
}
238+
239+
impl<T> AuxiliaryResolver<T> {
240+
pub fn new_normal(resolver: T) -> Self {
241+
Self::Resolver(resolver)
242+
}
243+
244+
pub fn new_dhcp(iface: &str) -> Result<Self, DnsError> {
245+
let record = DhcpDnsRecord::new(iface)?;
246+
Ok(Self::Dhcp(Mutex::new(record)))
247+
}
248+
}

0 commit comments

Comments
 (0)