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