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