1use crate::{Error, IpType, Provider, Result};
24use async_trait::async_trait;
25use derive_deref::Deref;
26use log::{debug, trace};
27use reqwest::{Client, Proxy};
28use serde::Deserialize;
29use serde_json::Value;
30use std::default::Default;
31use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
32use std::str::FromStr;
33use std::time::Duration;
34use thiserror::Error as ErrorDerive;
35use trust_dns_resolver::{
36 config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts},
37 error::ResolveError,
38 TokioAsyncResolver,
39};
40
41#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
43pub enum ProviderMethod {
44 #[serde(rename = "plain")]
46 Plain,
47 #[serde(rename = "json")]
49 Json,
50 #[serde(rename = "dns")]
52 Dns,
53}
54
55#[derive(Clone, Debug, Deserialize, PartialEq)]
57pub struct ProviderInfo {
58 name: String,
60 #[serde(rename = "type")]
62 addr_type: IpType,
63 method: ProviderMethod,
65 url: String,
66 key: Option<String>,
67}
68
69impl Default for ProviderInfo {
70 fn default() -> Self {
71 Self {
72 name: String::default(),
73 addr_type: IpType::Ipv4,
74 method: ProviderMethod::Plain,
75 url: String::default(),
76 key: None,
77 }
78 }
79}
80
81#[derive(Debug, ErrorDerive)]
83pub enum GlobalIpError {
84 #[error(transparent)]
86 ReqwestError(#[from] reqwest::Error),
87 #[error(transparent)]
89 JsonParseError(#[from] serde_json::Error),
90 #[error("field `{0}' does not exist in response")]
92 JsonNotFoundError(String),
93 #[error("field `{0}' in response can't be decoded")]
95 JsonDecodeError(String),
96 #[error(transparent)]
98 DnsError(#[from] Box<ResolveError>),
99 #[error("specified DNS server `{0}' has no address")]
101 DnsNoServerError(String),
102}
103
104macro_rules! make_get_type {
105 () => {
106 fn get_type(&self) -> IpType {
108 self.info.addr_type
109 }
110 };
111}
112
113macro_rules! make_new {
114 ($name: ident) => {
115 impl $name {
116 fn new(info: &ProviderInfo, timeout: u64, proxy: &Option<String>) -> Self {
118 Self(AbstractProvider {
119 info: info.clone(),
120 timeout,
121 proxy: proxy.clone(),
122 })
123 }
124 }
125 };
126}
127
128#[derive(Clone, Debug)]
130pub struct AbstractProvider {
131 pub info: ProviderInfo,
133 pub timeout: u64,
135 pub proxy: Option<String>,
137}
138
139impl Default for AbstractProvider {
140 fn default() -> Self {
141 Self {
142 info: ProviderInfo::default(),
143 timeout: 1000,
144 proxy: None,
145 }
146 }
147}
148
149fn build_client(timeout: u64, proxy: &Option<String>) -> reqwest::Result<Client> {
151 let client = match (timeout, proxy) {
152 (0, None) => Client::new(),
153 (0, Some(proxy)) => Client::builder().proxy(Proxy::all(proxy)?).build()?,
154 (_, None) => Client::builder()
155 .timeout(Duration::from_millis(timeout))
156 .build()?,
157 (_, Some(proxy)) => Client::builder()
158 .proxy(Proxy::all(proxy)?)
159 .timeout(Duration::from_millis(timeout))
160 .build()?,
161 };
162 Ok(client)
163}
164
165async fn build_client_get(url: &str, timeout: u64, proxy: &Option<String>) -> Result<String> {
167 Ok((async {
168 let client = build_client(timeout, proxy)?;
169 debug!("Reqwesting {:?} through proxy {:?}", url, proxy);
170 client
171 .get(url)
172 .send()
173 .await?
174 .error_for_status()?
175 .text()
176 .await
177 })
178 .await
179 .map_err(GlobalIpError::ReqwestError)?)
180}
181
182fn create_ipaddr(addr: &str, addr_type: IpType) -> Result<IpAddr> {
184 Ok(match addr_type {
185 IpType::Ipv4 => IpAddr::V4(Ipv4Addr::from_str(addr).map_err(Error::AddrParseError)?),
186 IpType::Ipv6 => IpAddr::V6(Ipv6Addr::from_str(addr).map_err(Error::AddrParseError)?),
187 })
188}
189
190#[derive(Clone, Debug, Deref)]
192pub struct ProviderPlain(AbstractProvider);
193
194make_new! {ProviderPlain}
195
196#[async_trait]
197impl Provider for ProviderPlain {
198 async fn get_addr(&self) -> Result<IpAddr> {
199 let addr = build_client_get(&self.info.url, self.timeout, &self.proxy).await?;
200 debug!("Plain provider {:?} returned {:?}", self.info, addr);
201 create_ipaddr(&addr, self.info.addr_type)
202 }
203
204 make_get_type! {}
205}
206
207#[derive(Clone, Debug, Deref)]
209pub struct ProviderJson(AbstractProvider);
210
211make_new! {ProviderJson}
212
213#[async_trait]
214impl Provider for ProviderJson {
215 async fn get_addr(&self) -> Result<IpAddr> {
216 let resp = build_client_get(&self.info.url, self.timeout, &self.proxy).await?;
217 trace!("Provider got response {:?}", resp);
218 let json: Value = serde_json::from_str(&resp).map_err(GlobalIpError::JsonParseError)?;
220 let key = self
221 .info
222 .key
223 .clone()
224 .expect("`key' should exist for JSON providers");
225 let addr = json
227 .get(&key)
228 .ok_or_else(|| GlobalIpError::JsonNotFoundError(key.clone()))?
229 .as_str()
230 .ok_or(GlobalIpError::JsonDecodeError(key))?;
231 debug!("JSON provider {:?} returned {:?}", self.info, addr);
232 create_ipaddr(addr, self.info.addr_type)
233 }
234
235 make_get_type! {}
236}
237
238#[derive(Clone, Debug, Deref)]
240pub struct ProviderDns(AbstractProvider);
241
242make_new! {ProviderDns}
243
244async fn host_to_addr(
246 resolver: TokioAsyncResolver,
247 host: &str,
248 addr_type: IpType,
249) -> std::result::Result<Option<IpAddr>, ResolveError> {
250 Ok(match addr_type {
251 IpType::Ipv4 => {
252 let srv = resolver.ipv4_lookup(host).await?;
253 (|| Some(IpAddr::V4(*srv.iter().next()?)))()
255 }
256 IpType::Ipv6 => {
257 let srv = resolver.ipv6_lookup(host).await?;
258 (|| Some(IpAddr::V6(*srv.iter().next()?)))()
259 }
260 })
261}
262
263#[async_trait]
264impl Provider for ProviderDns {
265 async fn get_addr(&self) -> Result<IpAddr> {
266 let (query, server) = self
267 .info
268 .url
269 .split_once('@')
270 .expect("DNS Provider URL should be like query@server");
271 let mut opts = ResolverOpts::default();
272 opts.timeout = Duration::from_millis(self.timeout);
273 let opts = opts;
274 let resolver = TokioAsyncResolver::tokio(ResolverConfig::new(), opts)
276 .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?;
277 debug!("Resolving {:?} on {:?}", server, resolver);
278 let server_addr = host_to_addr(resolver, server, self.info.addr_type)
280 .await
281 .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?
283 .ok_or_else(|| GlobalIpError::DnsNoServerError(server.to_string()))?;
285 let ns = NameServerConfig {
287 socket_addr: std::net::SocketAddr::new(server_addr, 53),
288 protocol: Protocol::Udp,
289 tls_dns_name: None,
290 trust_nx_responses: false,
291 bind_addr: None,
292 };
293 let mut config = ResolverConfig::new();
294 config.add_name_server(ns);
295 let resolver = TokioAsyncResolver::tokio(config, opts)
297 .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?;
298 debug!("Resolving {:?} on {:?}", query, resolver);
299 let addr = host_to_addr(resolver, query, self.info.addr_type)
300 .await
301 .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?
302 .ok_or_else(|| GlobalIpError::DnsNoServerError(server.to_string()))?;
304 debug!("DNS provider {:?} returned {:?}", self.info, addr);
305 Ok(addr)
306 }
307
308 make_get_type! {}
309}
310
311#[derive(Debug)]
313pub struct ProviderMultiple {
314 providers: Vec<ProviderInfo>,
315 addr_type: IpType,
316 timeout: u64,
317 proxy: Option<String>,
318}
319
320impl Default for ProviderMultiple {
321 fn default() -> Self {
322 let providers: Vec<ProviderInfo> = serde_json::from_str(DEFAULT_PROVIDERS).unwrap();
323 Self {
324 providers,
325 addr_type: IpType::Ipv4,
326 timeout: 1000,
327 proxy: None,
328 }
329 }
330}
331
332impl ProviderMultiple {
333 #[must_use]
335 pub fn default_v6() -> Self {
336 Self {
337 addr_type: IpType::Ipv6,
338 ..ProviderMultiple::default()
339 }
340 }
341}
342
343#[async_trait]
344impl Provider for ProviderMultiple {
345 async fn get_addr(&self) -> Result<IpAddr> {
346 let mut result: Result<IpAddr> = Err(crate::Error::NoAddress);
347 trace!("Registered providers: {:?}", self.providers);
348 for info in &self.providers {
349 if info.addr_type != self.addr_type {
350 continue;
351 }
352 let this_result = match info.method {
353 ProviderMethod::Plain => {
354 let provider = ProviderPlain::new(info, self.timeout, &self.proxy);
355 provider.get_addr().await
356 }
357 ProviderMethod::Json => {
358 let provider = ProviderJson::new(info, self.timeout, &self.proxy);
359 provider.get_addr().await
360 }
361 ProviderMethod::Dns => {
362 let provider = ProviderDns::new(info, self.timeout, &self.proxy);
363 provider.get_addr().await
364 }
365 };
366 if this_result.is_ok() {
367 debug!("Using result {:?} from provider {:?}", this_result, info);
368 result = this_result;
369 break;
370 }
371 }
372 result
373 }
374
375 fn get_type(&self) -> IpType {
376 self.addr_type
377 }
378}
379
380pub const DEFAULT_PROVIDERS: &str = r#"[
382 {
383 "method": "plain",
384 "name": "ipify",
385 "type": "IPv4",
386 "url": "https://api.ipify.org/"
387 },
388 {
389 "method": "plain",
390 "name": "ipify",
391 "type": "IPv6",
392 "url": "https://api6.ipify.org/"
393 },
394 {
395 "method": "plain",
396 "name": "ipv6-test",
397 "type": "IPv4",
398 "url": "https://v4.ipv6-test.com/api/myip.php"
399 },
400 {
401 "method": "plain",
402 "name": "ipv6-test",
403 "type": "IPv6",
404 "url": "https://v6.ipv6-test.com/api/myip.php"
405 },
406 {
407 "method": "plain",
408 "name": "ident.me",
409 "type": "IPv4",
410 "url": "https://v4.ident.me/"
411 },
412 {
413 "method": "plain",
414 "name": "ident.me",
415 "type": "IPv6",
416 "url": "https://v6.ident.me/"
417 },
418 {
419 "key": "ip",
420 "method": "json",
421 "name": "test-ipv6",
422 "padding": "callback",
423 "type": "IPv4",
424 "url": "https://ipv4.test-ipv6.com/ip/"
425 },
426 {
427 "key": "ip",
428 "method": "json",
429 "name": "test-ipv6",
430 "padding": "callback",
431 "type": "IPv6",
432 "url": "https://ipv6.test-ipv6.com/ip/"
433 },
434 {
435 "method": "dns",
436 "name": "opendns.com",
437 "type": "IPv4",
438 "url": "myip.opendns.com@resolver1.opendns.com"
439 },
440 {
441 "method": "dns",
442 "name": "opendns.com",
443 "type": "IPv6",
444 "url": "myip.opendns.com@resolver1.opendns.com"
445 },
446 {
447 "method": "dns",
448 "name": "akamai.com",
449 "type": "IPv4",
450 "url": "whoami.akamai.com@ns1-1.akamaitech.net"
451 },
452 {
453 "method": "plain",
454 "name": "akamai.com",
455 "type": "IPv4",
456 "url": "http://whatismyip.akamai.com"
457 },
458 {
459 "method": "plain",
460 "name": "akamai.com",
461 "type": "IPv6",
462 "url": "http://ipv6.whatismyip.akamai.com"
463 }
464]"#;