external_ip/sources/
http.rs1use crate::sources::interfaces::{Error, Family, IpFuture, IpResult, Source};
2use log::trace;
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use std::time::Duration;
5
6pub struct HTTPSourceBuilder {
7 url: String,
8 timeout: Duration,
9 family: Family,
10}
11impl HTTPSourceBuilder {
12 pub fn new<S: Into<String>>(url: S) -> Self {
13 Self {
14 url: url.into(),
15 timeout: Duration::from_secs(30),
16 family: Family::Any,
17 }
18 }
19 pub fn with_timeout(mut self, timeout: Duration) -> Self {
20 self.timeout = timeout;
21 self
22 }
23 pub fn with_supported_family(mut self, family: Family) -> Self {
24 self.family = family;
25 self
26 }
27 pub fn build(self) -> HTTPSource {
28 let Self {
29 url,
30 timeout,
31 family,
32 } = self;
33 HTTPSource {
34 url,
35 timeout,
36 family,
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
46pub struct HTTPSource {
47 url: String,
48 timeout: Duration,
49 family: Family,
50}
51
52impl Source for HTTPSource {
53 fn get_ip(&self, family: Family) -> IpFuture<'_> {
54 async fn run(_self: &HTTPSource, family: Family) -> IpResult {
55 if !((_self.family == Family::Any)
56 || (family == Family::Any)
57 || (_self.family == family))
58 {
59 return Err(Error::UnsupportedFamily);
60 }
61
62 trace!("Contacting {:?}", _self.url);
63 let client = reqwest::Client::builder().timeout(_self.timeout);
64 let client = match family {
65 Family::IPv4 => client.local_address(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),
66 Family::IPv6 => {
67 client.local_address(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)))
68 }
69 Family::Any => client,
70 }
71 .build()?;
72 let resp = client.get(&_self.url).send().await?.text().await?;
73 let parsed_ip: IpAddr = resp.trim().parse()?;
74 match (family, parsed_ip) {
75 (Family::Any, _)
76 | (Family::IPv4, IpAddr::V4(_))
77 | (Family::IPv6, IpAddr::V6(_)) => Ok(parsed_ip),
78 _ => Err(Error::UnsupportedFamily),
79 }
80 }
81
82 Box::pin(run(self, family))
83 }
84
85 fn box_clone(&self) -> Box<dyn Source> {
86 Box::new(self.clone())
87 }
88}
89
90impl std::fmt::Display for HTTPSource {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "HttpSource: {}", self.url)
93 }
94}
95
96pub fn get_http_sources<T>() -> T
98where
99 T: std::iter::FromIterator<Box<dyn Source>>,
100{
101 [
102 ("https://icanhazip.com/", Family::Any),
103 ("https://myexternalip.com/raw", Family::Any),
104 ("https://ifconfig.io/ip", Family::Any),
105 ("https://ipecho.net/plain", Family::Any),
106 ("https://checkip.amazonaws.com/", Family::IPv4),
107 ("https://ident.me/", Family::Any),
108 ("http://whatismyip.akamai.com/", Family::IPv4),
109 ("https://myip.dnsomatic.com/", Family::IPv4),
110 ("https://api.ipify.org", Family::IPv4),
111 ("https://ifconfig.me/ip", Family::Any),
112 ("https://ipinfo.io/ip", Family::IPv4),
113 ("https://ip2location.io/ip", Family::Any),
114 ]
115 .iter()
116 .cloned()
117 .map(|(url, family)| {
118 HTTPSourceBuilder::new(url)
119 .with_supported_family(family)
120 .build()
121 })
122 .map(|x| -> Box<dyn Source> { Box::new(x) })
123 .collect()
124}