Skip to main content

ip_discovery/
lib.rs

1//! # ip-discovery
2//!
3//! A lightweight, high-performance Rust library for detecting public IP addresses
4//! via DNS, HTTP, and STUN protocols with fallback support.
5//!
6//! ## Features
7//!
8//! - **Multi-protocol support**: DNS, HTTP/HTTPS, STUN (RFC 5389)
9//! - **Trusted providers**: Google, Cloudflare, AWS, OpenDNS
10//! - **Fallback mechanism**: Automatic retry with different providers
11//! - **Flexible strategies**: First success, race (fastest), or consensus
12//! - **Zero-dependency protocols**: DNS and STUN use raw UDP sockets
13//! - **Async-first**: Built on tokio for high performance
14//!
15//! ## Quick Start
16//!
17//! ```rust,no_run
18//! use ip_discovery::{get_ip, get_ipv4, get_ipv6};
19//!
20//! #[tokio::main]
21//! async fn main() {
22//!     // Get any IP address (IPv4 or IPv6)
23//!     if let Ok(result) = get_ip().await {
24//!         println!("Public IP: {} (via {})", result.ip, result.provider);
25//!     }
26//!
27//!     // Get IPv4 specifically
28//!     if let Ok(result) = get_ipv4().await {
29//!         println!("IPv4: {}", result.ip);
30//!     }
31//! }
32//! ```
33//!
34//! ## Custom Configuration
35//!
36//! ```rust,no_run
37//! use ip_discovery::{Config, Strategy, Protocol, BuiltinProvider, get_ip_with};
38//! use std::time::Duration;
39//!
40//! #[tokio::main]
41//! async fn main() {
42//!     // Use only DNS providers with race strategy
43//!     let config = Config::builder()
44//!         .protocols(&[Protocol::Dns])
45//!         .strategy(Strategy::Race)
46//!         .timeout(Duration::from_secs(5))
47//!         .build();
48//!
49//!     if let Ok(result) = get_ip_with(config).await {
50//!         println!("IP: {} (latency: {:?})", result.ip, result.latency);
51//!     }
52//!
53//!     // Or pick specific providers
54//!     let config = Config::builder()
55//!         .providers(&[
56//!             BuiltinProvider::CloudflareDns,
57//!             BuiltinProvider::GoogleStun,
58//!         ])
59//!         .build();
60//!
61//!     if let Ok(result) = get_ip_with(config).await {
62//!         println!("IP: {}", result.ip);
63//!     }
64//! }
65//! ```
66
67#![warn(missing_docs)]
68
69mod config;
70mod error;
71mod provider;
72mod resolver;
73mod types;
74
75#[cfg(feature = "dns")]
76pub mod dns;
77
78#[cfg(feature = "http")]
79pub mod http;
80
81#[cfg(feature = "stun")]
82pub mod stun;
83
84pub use config::{Config, ConfigBuilder, Strategy};
85pub use error::{Error, ProviderError};
86pub use provider::Provider;
87pub use resolver::Resolver;
88pub use types::{BuiltinProvider, IpVersion, Protocol, ProviderResult};
89
90/// Get public IP address using default configuration.
91///
92/// Uses all available protocols with the [`Strategy::First`] fallback strategy
93/// and a 10-second per-provider timeout.
94///
95/// # Errors
96///
97/// Returns [`Error::AllProvidersFailed`] if every provider fails.
98pub async fn get_ip() -> Result<ProviderResult, Error> {
99    let config = Config::default();
100    get_ip_with(config).await
101}
102
103/// Get public IPv4 address using default configuration.
104///
105/// # Errors
106///
107/// Returns [`Error::AllProvidersFailed`] if no provider returns an IPv4 address.
108pub async fn get_ipv4() -> Result<ProviderResult, Error> {
109    let config = Config::builder().version(IpVersion::V4).build();
110    get_ip_with(config).await
111}
112
113/// Get public IPv6 address using default configuration.
114///
115/// # Errors
116///
117/// Returns [`Error::NoProvidersForVersion`] if no provider supports IPv6.
118pub async fn get_ipv6() -> Result<ProviderResult, Error> {
119    let config = Config::builder().version(IpVersion::V6).build();
120    get_ip_with(config).await
121}
122
123/// Get public IP address with a custom [`Config`].
124///
125/// # Errors
126///
127/// Returns an [`Error`] variant depending on the strategy and provider results.
128pub async fn get_ip_with(config: Config) -> Result<ProviderResult, Error> {
129    let resolver = Resolver::new(config);
130    resolver.resolve().await
131}