external_ip/sources/
dns.rs1use crate::sources::interfaces::{Error, Family, IpFuture, IpResult, Source};
2use log::trace;
3
4use hickory_resolver::TokioResolver;
5use hickory_resolver::config::*;
6use hickory_resolver::net::runtime::TokioRuntimeProvider;
7use hickory_resolver::proto::rr::RData;
8
9#[derive(Debug, Clone, Copy)]
10pub enum QueryType {
11 TXT,
12 A,
13 AAAA,
14}
15
16#[derive(Debug, Clone)]
22pub struct DNSSource {
23 server: String,
24 record_type: QueryType,
25 record: String,
26}
27
28impl DNSSource {
29 pub fn new<S: Into<String>, R: Into<String>>(
30 server: S,
31 record_type: QueryType,
32 record: R,
33 ) -> Self {
34 DNSSource {
35 server: server.into(),
36 record_type,
37 record: record.into(),
38 }
39 }
40 fn source<R: Into<String>>(
41 server: String,
42 record_type: QueryType,
43 record: R,
44 ) -> Box<dyn Source> {
45 Box::new(DNSSource {
46 server,
47 record_type,
48 record: record.into(),
49 })
50 }
51}
52
53impl std::fmt::Display for DNSSource {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 write!(
56 f,
57 "DnsSource: {:?} {:?} {}",
58 self.server, self.record_type, self.record
59 )
60 }
61}
62
63impl DNSSource {
64 async fn get_resolver(self: &DNSSource, family: Family) -> Result<TokioResolver, Error> {
65 let mut resolver_opts = ResolverOpts::default();
66 resolver_opts.ip_strategy = match family {
67 Family::IPv4 => LookupIpStrategy::Ipv4Only,
68 Family::IPv6 => LookupIpStrategy::Ipv6Only,
69 Family::Any => resolver_opts.ip_strategy,
70 };
71
72 trace!(
73 "Bootstrapping resolver for {} with strategy {:?}",
74 self.server, resolver_opts.ip_strategy
75 );
76 let mut builder = TokioResolver::builder_with_config(
77 ResolverConfig::udp_and_tcp(&GOOGLE),
78 TokioRuntimeProvider::default(),
79 );
80 *builder.options_mut() = resolver_opts.clone();
81 let resolver = builder.build()?;
82
83 let mut name_servers = Vec::new();
84 for found_ip in resolver.lookup_ip(&self.server).await?.iter() {
85 trace!("DNS address {}", found_ip);
86 name_servers.push(NameServerConfig::udp(found_ip));
87 }
88
89 let config = ResolverConfig::from_parts(None, Vec::new(), name_servers);
90
91 let mut builder =
92 TokioResolver::builder_with_config(config, TokioRuntimeProvider::default());
93 *builder.options_mut() = resolver_opts;
94 Ok(builder.build()?)
95 }
96}
97
98impl Source for DNSSource {
99 fn get_ip(&self, family: Family) -> IpFuture<'_> {
100 async fn run(_self: &DNSSource, family: Family) -> IpResult {
101 if matches!(
102 (family, _self.record_type),
103 (Family::IPv4, QueryType::AAAA) | (Family::IPv6, QueryType::A)
104 ) {
105 return Err(Error::UnsupportedFamily);
106 }
107 trace!("Contacting {:?} for {}", _self.server, _self.record);
108 let resolver: TokioResolver = _self
109 .get_resolver(match _self.record_type {
110 QueryType::A => Family::IPv4,
111 QueryType::AAAA => Family::IPv6,
112 _ => family,
113 })
114 .await?;
115
116 match _self.record_type {
117 QueryType::TXT => {
118 for reply in resolver.txt_lookup(_self.record.clone()).await?.answers() {
119 if let RData::TXT(txt) = &reply.data {
120 for txt in txt.txt_data.iter() {
121 if let Ok(data) = std::str::from_utf8(txt) {
122 let ip: std::net::IpAddr = data.parse()?;
123 if family == Family::Any
124 || (family == Family::IPv4 && ip.is_ipv4())
125 || (family == Family::IPv6 && ip.is_ipv6())
126 {
127 return Ok(ip);
128 }
129 }
130 }
131 }
132 }
133 }
134 QueryType::A => {
135 if family == Family::IPv4 || family == Family::Any {
136 for reply in resolver.lookup_ip(_self.record.clone()).await?.iter() {
137 if reply.is_ipv4() {
138 return Ok(reply);
139 }
140 }
141 }
142 return Err(Error::UnsupportedFamily);
143 }
144 QueryType::AAAA => {
145 if family == Family::IPv6 || family == Family::Any {
146 for reply in resolver.lookup_ip(_self.record.clone()).await?.iter() {
147 if reply.is_ipv6() {
148 return Ok(reply);
149 }
150 }
151 }
152 return Err(Error::UnsupportedFamily);
153 }
154 }
155 Err(Error::DnsResolutionEmpty)
156 }
157 Box::pin(run(self, family))
158 }
159
160 fn box_clone(&self) -> Box<dyn Source> {
161 Box::new(self.clone())
162 }
163}
164
165pub fn get_dns_sources<T>() -> T
167where
168 T: std::iter::FromIterator<Box<dyn Source>>,
169{
170 vec![
171 DNSSource::source(
172 String::from("resolver1.opendns.com"),
173 QueryType::A,
174 "myip.opendns.com",
175 ),
176 DNSSource::source(
177 String::from("resolver1.opendns.com"),
178 QueryType::AAAA,
179 "myip.opendns.com",
180 ),
181 DNSSource::source(
182 String::from("ns1.google.com"),
183 QueryType::TXT,
184 "o-o.myaddr.l.google.com",
185 ),
186 ]
187 .into_iter()
188 .collect()
189}