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//! - **Synchronous & Asynchronous**: Zero-dependency blocking API by default, or Tokio async via feature
14//!
15//! ## Synchronous (Blocking) Quick Start
16//!
17//! ```rust,no_run
18//! use ip_discovery::blocking::{get_ip, get_ipv4};
19//!
20//! // Get any IP address (IPv4 or IPv6) synchronously without Tokio runtime
21//! if let Ok(result) = get_ip() {
22//! println!("Public IP: {} (via {})", result.ip, result.provider);
23//! }
24//!
25//! // Get IPv4 specifically
26//! if let Ok(result) = get_ipv4() {
27//! println!("IPv4: {}", result.ip);
28//! }
29//! ```
30//!
31//! ## Asynchronous Quick Start (with `tokio` feature)
32//!
33//! ```rust,no_run
34//! # #[cfg(feature = "tokio")]
35//! # #[tokio::main]
36//! # async fn main() {
37//! use ip_discovery::{get_ip, get_ipv4};
38//!
39//! // Get any IP address asynchronously
40//! if let Ok(result) = get_ip().await {
41//! println!("Public IP: {} (via {})", result.ip, result.provider);
42//! }
43//! # }
44//! # #[cfg(not(feature = "tokio"))]
45//! # fn main() {}
46//! ```
47
48#![warn(missing_docs)]
49
50pub mod blocking;
51mod config;
52mod error;
53mod provider;
54#[cfg(feature = "tokio")]
55mod resolver;
56mod types;
57
58#[cfg(feature = "dns")]
59pub mod dns;
60
61#[cfg(feature = "http")]
62pub mod http;
63
64#[cfg(feature = "stun")]
65pub mod stun;
66
67pub use config::{Config, ConfigBuilder, Strategy};
68pub use error::{Error, ProviderError};
69pub use provider::{BlockingProvider, BoxedBlockingProvider};
70#[cfg(feature = "tokio")]
71pub use provider::{BoxedProvider, Provider};
72#[cfg(feature = "tokio")]
73pub use resolver::Resolver;
74pub use types::{BuiltinProvider, IpVersion, Protocol, ProviderResult};
75
76#[cfg(feature = "tokio")]
77/// Get public IP address using default configuration asynchronously.
78///
79/// Uses all available protocols with the [`Strategy::First`] fallback strategy
80/// and a 10-second per-provider timeout.
81///
82/// # Errors
83///
84/// Returns [`Error::AllProvidersFailed`] if every provider fails.
85pub async fn get_ip() -> Result<ProviderResult, Error> {
86 let config = Config::default();
87 get_ip_with(config).await
88}
89
90#[cfg(feature = "tokio")]
91/// Get public IPv4 address using default configuration asynchronously.
92///
93/// # Errors
94///
95/// Returns [`Error::AllProvidersFailed`] if no provider returns an IPv4 address.
96pub async fn get_ipv4() -> Result<ProviderResult, Error> {
97 let config = Config::builder().version(IpVersion::V4).build();
98 get_ip_with(config).await
99}
100
101#[cfg(feature = "tokio")]
102/// Get public IPv6 address using default configuration asynchronously.
103///
104/// # Errors
105///
106/// Returns [`Error::NoProvidersForVersion`] if no provider supports IPv6.
107pub async fn get_ipv6() -> Result<ProviderResult, Error> {
108 let config = Config::builder().version(IpVersion::V6).build();
109 get_ip_with(config).await
110}
111
112#[cfg(feature = "tokio")]
113/// Get public IP address with a custom [`Config`] asynchronously.
114///
115/// # Errors
116///
117/// Returns an [`Error`] variant depending on the strategy and provider results.
118pub async fn get_ip_with(config: Config) -> Result<ProviderResult, Error> {
119 let resolver = Resolver::new(config);
120 resolver.resolve().await
121}
122
123/// Get the primary local private IPv4 address.
124///
125/// This function queries the OS routing table by creating a connectionless
126/// UDP socket and connecting it to a public destination. No network packets are sent.
127pub fn get_private_ip() -> Option<std::net::IpAddr> {
128 let socket = std::net::UdpSocket::bind("0.0.0.0:0").ok()?;
129 // Using Google DNS to trigger routing table lookup for outbound IPv4 traffic.
130 socket.connect("8.8.8.8:80").ok()?;
131 socket.local_addr().ok().map(|addr| addr.ip())
132}
133
134/// Get the primary local private IPv6 address.
135///
136/// This function queries the OS routing table by creating a connectionless
137/// UDP socket and connecting it to a public destination. No network packets are sent.
138pub fn get_private_ipv6() -> Option<std::net::IpAddr> {
139 let socket = std::net::UdpSocket::bind("[::]:0").ok()?;
140 // Using Google DNS IPv6 to trigger routing table lookup for outbound IPv6 traffic.
141 socket.connect("[2001:4860:4860::8888]:80").ok()?;
142 socket.local_addr().ok().map(|addr| addr.ip())
143}