http_ip/lib.rs
1//! Utilities to determine HTTP client's IP
2//!
3//! ## Features
4//!
5//! - `http` - Enables filter implementation using http's header map;
6//! - `axum08` - Enables `axum` extractor implementation for `0.8.x`;
7//! - `tonic014` - Enables `tonic` extension implementation for `0.14.x`.
8//! - `ohkami024` - Enables `ohkami` extension implementation for `0.24.x`.
9//!
10//! ## Example
11//!
12//! A very simple example to extract IP from given header value using CIDR filtering to determine client's IP
13//!
14//! This is different from a very common approach selecting leftmost IP as client's IP
15//! Instead you can search starting through the right, filtering out your cloud's CIDRs to guarantee you get client's real external IP.
16//! In complicated network environments individual clients are rarely having static IPs and
17//! most likely hidden by corporate proxy that shields company's network, which often inserts extra IP in between you and client.
18//!
19//! This in-between IP is more often than not, what you'd want to get and use, rather than leftmost
20//! (which may be client IP, but it is not client IP from perspective of public network)
21//!
22//!```rust
23//!
24//!const IPS: &str = "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348,198.51.100.178";
25//!const CIDR: http_ip::filter::Cidr = match http_ip::filter::Cidr::from_text("198.51.100.0/24") {
26//! Ok(cidr) => cidr,
27//! Err(_) => panic!("I cannot fail"),
28//!};
29//!
30//!//Get ips in reverse (from right) order to filter out proxy IPs manually until we reach client's IP
31//!let ips = http_ip::forwarded::parse_x_forwarded_for_rev(IPS);
32//!let client_ip = http_ip::find_next_ip_after_filter(ips, &CIDR).expect("to find ip");
33//!assert_eq!(client_ip, core::net::IpAddr::V6(core::net::Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x8d3, 0x1319, 0x8a2e, 0x370, 0x7348)));
34//!```
35
36#![no_std]
37#![warn(missing_docs)]
38#![allow(clippy::style)]
39
40use core::net::IpAddr;
41
42#[cfg(any(feature = "tonic014", feature = "http"))]
43mod shared;
44pub mod forwarded;
45pub mod filter;
46#[cfg(feature = "http")]
47pub mod http;
48#[cfg(feature = "axum08")]
49pub mod axum08;
50#[cfg(feature = "tonic014")]
51pub mod tonic014;
52#[cfg(feature = "ohkami024")]
53pub mod ohkami024;
54
55#[inline]
56///Determines next IP among `nodes` iterator after applying filter
57///
58///If `node` is not IP address, then search is aborted, as it is impossible to correctly apply filter
59pub fn find_next_ip_after_filter<'a>(nodes: impl Iterator<Item = forwarded::ForwardedNode<'a>>, filter: &impl filter::Filter) -> Option<IpAddr> {
60
61 for node in nodes {
62 match node {
63 forwarded::ForwardedNode::Ip(ip) => if filter.is_match(ip) {
64 continue
65 } else {
66 return Some(ip);
67 },
68 _ => return None,
69 }
70 }
71
72 None
73}