getip/
gip.rs

1//! Implementations of `Provider` that receive global IP addresses from online services.
2//! This is an asynchronous rewrite of dalance/gip.
3//
4//  Copyright (C) 2018 dalance
5//  Copyright (C) 2021 Zhang Maiyun <me@maiyun.me>
6//
7//  This file is part of DNS updater.
8//
9//  DNS updater is free software: you can redistribute it and/or modify
10//  it under the terms of the GNU Affero General Public License as published by
11//  the Free Software Foundation, either version 3 of the License, or
12//  (at your option) any later version.
13//
14//  DNS updater is distributed in the hope that it will be useful,
15//  but WITHOUT ANY WARRANTY; without even the implied warranty of
16//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17//  GNU Affero General Public License for more details.
18//
19//  You should have received a copy of the GNU Affero General Public License
20//  along with DNS updater.  If not, see <https://www.gnu.org/licenses/>.
21//
22
23use 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/// Method with which a global `Provider` retrieves addresses.
39#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
40pub enum ProviderMethod {
41    /// Plain text HTTP request.
42    #[serde(rename = "plain")]
43    Plain,
44    /// HTTP JSON response.
45    #[serde(rename = "json")]
46    Json,
47    /// DNS queries.
48    #[serde(rename = "dns")]
49    Dns,
50}
51
52/// Information and configuration of a `Provider`.
53#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
54pub struct ProviderInfo {
55    /// Provider name.
56    name: String,
57    /// Provider type of its address.
58    #[serde(rename = "type")]
59    addr_type: IpType,
60    /// Method used by this provider.
61    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/// Error type of global IP providers.
79#[derive(Debug, ErrorDerive)]
80pub enum GlobalIpError {
81    /// Error because of a `reqwest` request.
82    #[error(transparent)]
83    ReqwestError(#[from] reqwest::Error),
84    /// Error during JSON deserialization.
85    #[error(transparent)]
86    JsonParseError(#[from] serde_json::Error),
87    /// Specified JSON field does not exist in the response.
88    #[error("field `{0}' does not exist in response")]
89    JsonNotFoundError(String),
90    /// Specified JSON field cannot be decoded.
91    #[error("field `{0}' in response can't be decoded")]
92    JsonDecodeError(String),
93    /// DNS queries failed.
94    #[error(transparent)]
95    DnsError(#[from] Box<ResolveError>),
96    /// Cannot resolve the address of the DNS server itself.
97    #[error("specified DNS server `{0}' has no address")]
98    DnsNoServerError(String),
99}
100
101macro_rules! make_get_type {
102    () => {
103        /// Get the `IPType` that this provider returns.
104        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            /// Create a new $name.
114            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/// Shared fields among all providers.
126#[derive(Clone, Debug)]
127pub struct AbstractProvider {
128    /// Definition of this provider.
129    pub info: ProviderInfo,
130    /// DNS query or HTTP request timeout.
131    pub timeout: u64,
132    /// Proxy for HTTP requests.
133    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
146/// Build a new client with `timeout` and `proxy`.
147fn 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
162/// Build a new GET request to `url` with `timeout` and `proxy` and return the response.
163async 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
179/// Create a `getip::Result` containing the IP address.
180fn 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/// Plain text provider.
188#[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/// JSON provider.
205#[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        // Try to parse the response as JSON
216        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        // Extract ip from response
223        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/// DNS provider.
236#[derive(Clone, Debug, Deref)]
237pub struct ProviderDns(AbstractProvider);
238
239make_new! {ProviderDns}
240
241/// Resolve host to address with a resolver.
242async 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        // First get the address of the DNS server
270        let resolver = TokioResolver::builder_tokio()
271            .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?
272            .build();
273        debug!("Resolving {server:?} on {resolver:?}");
274        // Get Resolver's address
275        let server_addr = host_to_addr(resolver, server, self.info.addr_type)
276            .await
277            // Deal with errors
278            .map_err(|e| GlobalIpError::DnsError(Box::new(e)))?
279            // Deal with Nones
280            .ok_or_else(|| GlobalIpError::DnsNoServerError(server.to_string()))?;
281        // Construct Resolve config
282        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        // Create new resolver
293        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            // Deal with Nones
301            .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/// Try multiple providers until anyone succeeds
310#[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    /// A default IPv6 provider
332    #[must_use]
333    pub fn default_v6() -> Self {
334        Self {
335            addr_type: IpType::Ipv6,
336            ..Self::default()
337        }
338    }
339
340    /// Set your own providers from JSON
341    ///
342    /// # Errors
343    /// Fails with the same error as `serde_json::from_str`
344    /// if the JSON is invalid.
345    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
392/// JSON list of default providers that `ProviderDefault` uses.
393pub 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]"#;