1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Crate providing a generic way to find the public IP address of the
//! device it runs on, along with with some built in resolvers
//!
//! # Basic Usage
//!
//! ```rust
//! use async_std::task;
//! use public_ip::{dns, http, ToResolver, BoxToResolver};
//!
//! // List of resolvers to try and get an IP address from
//! let resolver = vec![
//!     BoxToResolver::new(dns::OPENDNS_RESOLVER),
//!     BoxToResolver::new(http::HTTP_IPIFY_ORG_RESOLVER),
//! ].to_resolver();
//! // Attempt to get an IP address and print it
//! if let Some(ip) = task::block_on(public_ip::resolve_address(resolver)) {
//!     println!("public ip address: {:?}", ip);
//! } else {
//!     println!("couldn't get an IP address");
//! }
//! ```
//!
//! # Usage with Resolution
//!
//! ```rust
//! use std::any::Any;
//!
//! use async_std::task;
//! use public_ip::{dns, ToResolver, Resolution};
//!  
//! // List of resolvers to try and get an IP address from
//! let resolver = dns::OPENDNS_RESOLVER.to_resolver();
//! // Attempt to get an IP address and print it
//! if let Some(resolution) = task::block_on(public_ip::resolve(resolver)) {
//!     if let Some(resolution) = Any::downcast_ref::<dns::DnsResolution>(&resolution) {
//!         println!("public ip address {:?} resolved from {:?} ({:?})",
//!             resolution.address(),
//!             resolution.name(),
//!             resolution.server(),
//!         );
//!     }
//! } else {
//!     println!("couldn't get an IP address");
//! }
//! ```

mod resolver;

#[cfg(any(feature = "http-resolver", feature = "dns-resolver"))]
mod util;

#[cfg(feature = "dns-resolver")]
pub mod dns;

#[cfg(feature = "http-resolver")]
pub mod http;

use std::any::Any;
use std::fmt::Debug;
use std::net::IpAddr;

use futures::stream::{BoxStream, StreamExt};

pub use crate::resolver::*;

/// Boxed `dyn Resolution`
pub type BoxResolution = Box<dyn Resolution>;
/// Boxed `dyn ResolutionError`
pub type BoxResolutionError = Box<dyn ResolutionError>;
/// Boxed `dyn Stream<Item = Result<BoxResolution, BoxResolutionError>>`
pub type BoxResolutionStream = BoxStream<'static, Result<BoxResolution, BoxResolutionError>>;

///////////////////////////////////////////////////////////////////////////////

/// The successful product of a resolver
///
/// As well as containing the IP address resolved,
/// resolvers will contain the specific parameters
/// used in the resolution in their concrete type.
/// Using `Any` allows you to downcast them to the
/// specific type and retrive them.
pub trait Resolution: Send + Any {
    /// The IP address resolved
    fn address(&self) -> IpAddr;
}

impl Resolution for Box<dyn Resolution> {
    fn address(&self) -> IpAddr {
        self.as_ref().address()
    }
}

///////////////////////////////////////////////////////////////////////////////

/// An error produced while attempting to resolve
pub trait ResolutionError: Send + Any + Debug {}

impl<T> ResolutionError for T where T: Send + Any + Debug {}

///////////////////////////////////////////////////////////////////////////////

/// Attempts resolve a single address (best effort)
pub async fn resolve_address<R>(resolver: R) -> Option<IpAddr>
where
    R: Resolver<DefaultResolverContext> + Unpin,
    R::Stream: Unpin,
{
    resolve(resolver).await.as_ref().map(Resolution::address)
}

/// Attempts to resolve to a resolution (best effort)
pub async fn resolve<R>(resolver: R) -> Option<R::Resolution>
where
    R: Resolver<DefaultResolverContext> + Unpin,
    R::Stream: Unpin,
{
    let mut resolution_stream = resolve_stream(resolver);
    loop {
        match resolution_stream.next().await {
            Some(Ok(resolution)) => return Some(resolution),
            Some(Err(_)) => continue,
            None => return None,
        }
    }
}

/// Resolves a stream with a default context
pub fn resolve_stream<R>(mut resolver: R) -> R::Stream
where
    R: Resolver<DefaultResolverContext>,
{
    (&mut resolver).resolve(DefaultResolverContext)
}