55 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			55 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
use std::net::IpAddr;
 | 
						|
 | 
						|
use rocket::http::Status;
 | 
						|
use rocket::request::{FromRequest, Outcome, Request};
 | 
						|
 | 
						|
#[derive(Debug)]
 | 
						|
pub struct ClientIP(pub String);
 | 
						|
 | 
						|
#[derive(Debug)]
 | 
						|
pub struct TrustedProxies(pub Vec<IpAddr>);
 | 
						|
 | 
						|
#[rocket::async_trait]
 | 
						|
impl<'r> FromRequest<'r> for ClientIP {
 | 
						|
    type Error = ();
 | 
						|
 | 
						|
    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
 | 
						|
        // fetch configured trusted proxies list (if any)
 | 
						|
        let trusted_proxies = &req.rocket().state::<TrustedProxies>().unwrap().0;
 | 
						|
 | 
						|
        // fetch the actual remote IP address
 | 
						|
        let remote_ip = match req.remote() {
 | 
						|
            Some(socket_addr) => socket_addr.ip(),
 | 
						|
            None => {
 | 
						|
                return Outcome::Error((Status::InternalServerError, ()));
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        // if the request does not come from one trusted proxy, don't trust any HTTP header
 | 
						|
        if !trusted_proxies.contains(&remote_ip) {
 | 
						|
            return Outcome::Success(ClientIP(remote_ip.to_string()));
 | 
						|
        }
 | 
						|
 | 
						|
        // fetch valid IP addresses from X-FORWARDED-FOR header(s)
 | 
						|
        let xff_ip_addresses: Vec<IpAddr> = req
 | 
						|
            .headers()
 | 
						|
            .get("x-forwarded-for")
 | 
						|
            .flat_map(|h| h.split(", "))
 | 
						|
            .flat_map(str::parse)
 | 
						|
            .collect::<Vec<_>>();
 | 
						|
 | 
						|
        // fetch Rocket's "client IP", which first inspects "X-REAL-IP" header
 | 
						|
        let client_ip = req.client_ip().unwrap();
 | 
						|
 | 
						|
        // find first (last) X-FORWARDED-FOR address that does not match one of the trusted proxies
 | 
						|
        Outcome::Success(ClientIP(
 | 
						|
            xff_ip_addresses
 | 
						|
                .iter()
 | 
						|
                .rev()
 | 
						|
                .find(|xff_ip_address| !trusted_proxies.contains(xff_ip_address))
 | 
						|
                .unwrap_or_else(|| xff_ip_addresses.first().unwrap_or(&client_ip))
 | 
						|
                .to_string(),
 | 
						|
        ))
 | 
						|
    }
 | 
						|
}
 |