qiniu_http_client/client/resolver/
trust_dns.rs1use super::{super::ResponseError, ResolveOptions, ResolveResult, Resolver};
2use async_std::task::block_on;
3use async_std_resolver::{resolver, resolver_from_system_conf, AsyncStdResolver as AsyncResolver};
4use futures::future::BoxFuture;
5use qiniu_http::ResponseErrorKind as HttpResponseErrorKind;
6use std::fmt;
7pub use trust_dns_resolver;
8use trust_dns_resolver::{
9 config::{ResolverConfig, ResolverOpts},
10 error::ResolveError,
11};
12
13#[cfg_attr(feature = "docs", doc(cfg(all(feature = "trust_dns", feature = "async"))))]
17#[derive(Clone)]
18pub struct TrustDnsResolver {
19 #[cfg(feature = "async")]
20 resolver: AsyncResolver,
21}
22
23type TrustDnsResolveResult<T> = Result<T, ResolveError>;
24
25impl TrustDnsResolver {
26 #[inline]
28 pub async fn new(config: ResolverConfig, options: ResolverOpts) -> TrustDnsResolveResult<Self> {
29 Ok(Self {
30 resolver: resolver(config, options).await?,
31 })
32 }
33
34 #[inline]
36 pub async fn default() -> TrustDnsResolveResult<Self> {
37 Ok(Self {
38 resolver: resolver(Default::default(), Default::default()).await?,
39 })
40 }
41
42 #[inline]
44 pub async fn from_system_conf() -> TrustDnsResolveResult<Self> {
45 Ok(Self {
46 resolver: resolver_from_system_conf().await?,
47 })
48 }
49}
50
51impl Resolver for TrustDnsResolver {
52 fn resolve(&self, domain: &str, opts: ResolveOptions) -> ResolveResult {
53 block_on(async move { self.async_resolve(domain, opts).await })
54 }
55
56 fn async_resolve<'a>(&'a self, domain: &'a str, opts: ResolveOptions<'a>) -> BoxFuture<'a, ResolveResult> {
57 Box::pin(async move {
58 Ok(self
59 .resolver
60 .lookup_ip(domain)
61 .await
62 .map_err(|err| convert_trust_dns_error_to_response_error(err, opts))?
63 .iter()
64 .collect::<Vec<_>>()
65 .into())
66 })
67 }
68}
69
70impl fmt::Debug for TrustDnsResolver {
71 #[inline]
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_struct("TrustDnsResolver").finish()
74 }
75}
76
77fn convert_trust_dns_error_to_response_error(err: ResolveError, opts: ResolveOptions) -> ResponseError {
78 let mut err = ResponseError::new(HttpResponseErrorKind::DnsServerError.into(), err);
79 if let Some(retried) = opts.retried() {
80 err = err.retried(retried);
81 }
82 err
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::test_utils::{make_record_set, make_zone, start_mock_dns_server};
89 use anyhow::{anyhow, Result as AnyResult};
90 use futures::future::abortable;
91 use std::{
92 collections::{HashMap, HashSet},
93 net::{IpAddr, Ipv4Addr, SocketAddr},
94 };
95 use tokio::{net::UdpSocket, task::spawn};
96 use trust_dns_resolver::config::{LookupIpStrategy, NameServerConfigGroup};
97 use trust_dns_server::{
98 authority::Catalog,
99 proto::rr::{Name, RData, RecordType},
100 ServerFuture,
101 };
102
103 const DEFAULT_TTL: u32 = 3600;
104
105 #[tokio::test]
106 async fn test_trust_dns_resolver() -> AnyResult<()> {
107 let (dns_server_addr, dns_server) = mock_dns_server().await?;
108 let (dns_server, abort_handle) = abortable(async move { dns_server.block_until_done().await });
109 let join_handle = spawn(dns_server);
110
111 let mut opts = ResolverOpts::default();
112 opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
113 let resolver = TrustDnsResolver::new(
114 ResolverConfig::from_parts(
115 None,
116 vec![],
117 NameServerConfigGroup::from_ips_clear([dns_server_addr.ip()].as_slice(), dns_server_addr.port(), true),
118 ),
119 opts,
120 )
121 .await?;
122
123 {
124 let ips = resolver.async_resolve("dns.alidns.com", Default::default()).await?;
125 assert_eq!(
126 make_set(ips.ip_addrs()),
127 make_set([
128 IpAddr::V4(Ipv4Addr::new(2, 3, 4, 5)),
129 IpAddr::V4(Ipv4Addr::new(3, 4, 5, 6)),
130 ])
131 );
132 }
133
134 {
135 let ips = resolver.async_resolve("alidns.com", Default::default()).await?;
136 assert_eq!(
137 make_set(ips.ip_addrs()),
138 make_set([IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)),])
139 );
140 }
141
142 {
143 let ips = resolver.async_resolve("www.alidns.com", Default::default()).await?;
144 assert_eq!(
145 make_set(ips.ip_addrs()),
146 make_set([IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)),])
147 );
148 }
149
150 abort_handle.abort();
151 let _ = join_handle.await?;
152 Ok(())
153 }
154
155 fn make_set(ips: impl AsRef<[IpAddr]>) -> HashSet<IpAddr> {
156 let mut h = HashSet::new();
157 h.extend(ips.as_ref());
158 h
159 }
160
161 async fn mock_dns_server() -> AnyResult<(SocketAddr, ServerFuture<Catalog>)> {
162 let root_name = Name::from_str_relaxed("alidns.com")?;
163 let root_record_set = make_record_set(
164 root_name.to_owned(),
165 RecordType::A,
166 DEFAULT_TTL,
167 [(root_name.to_owned(), DEFAULT_TTL, RData::A(Ipv4Addr::new(1, 2, 3, 4)))],
168 );
169 let sub_name = Name::from_str_relaxed("dns.alidns.com.")?;
170 let sub_record_set = make_record_set(
171 sub_name.to_owned(),
172 RecordType::A,
173 DEFAULT_TTL,
174 [
175 (sub_name.to_owned(), DEFAULT_TTL, RData::A(Ipv4Addr::new(2, 3, 4, 5))),
176 (sub_name.to_owned(), DEFAULT_TTL, RData::A(Ipv4Addr::new(3, 4, 5, 6))),
177 ],
178 );
179 let other_names = Name::from_str_relaxed("*.alidns.com.")?;
180 let other_record_set = make_record_set(
181 other_names.to_owned(),
182 RecordType::CNAME,
183 DEFAULT_TTL,
184 [(other_names.to_owned(), DEFAULT_TTL, RData::CNAME(root_name.to_owned()))],
185 );
186
187 let zone = make_zone(
188 root_name.to_owned(),
189 [
190 (root_name.to_owned(), RecordType::A, root_record_set),
191 (sub_name.to_owned(), RecordType::A, sub_record_set),
192 (other_names.to_owned(), RecordType::CNAME, other_record_set),
193 ],
194 )
195 .map_err(|err| anyhow!(err))?;
196 let udp_socket = UdpSocket::bind("127.0.0.1:0").await?;
197 let socket_addr = udp_socket.local_addr()?;
198 let mut zones = HashMap::with_capacity(2);
199 zones.insert(root_name, zone);
200 Ok((socket_addr, start_mock_dns_server(udp_socket, zones)))
201 }
202}