rocket_client_addr/
client_real_addr.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2
3use rocket::{
4    http::Status,
5    outcome::Outcome,
6    request::{self, FromRequest, Request},
7};
8
9/// The request guard used for getting an IP address from a client.
10#[derive(Debug, Clone)]
11pub struct ClientRealAddr {
12    /// IP address from a client.
13    pub ip: IpAddr,
14}
15
16fn from_request(request: &Request<'_>) -> Option<ClientRealAddr> {
17    match request.real_ip() {
18        Some(ip) => Some(ClientRealAddr {
19            ip,
20        }),
21        None => {
22            let forwarded_for_ip: Option<&str> = request.headers().get("x-forwarded-for").next(); // Only fetch the first one.
23
24            match forwarded_for_ip {
25                Some(forwarded_for_ip) => {
26                    let forwarded_for_ip = forwarded_for_ip.split(',').next(); // Only fetch the first one.
27
28                    match forwarded_for_ip {
29                        Some(forwarded_for_ip) => match forwarded_for_ip.trim().parse::<IpAddr>() {
30                            Ok(ip) => Some(ClientRealAddr {
31                                ip,
32                            }),
33                            Err(_) => request.remote().map(|addr| ClientRealAddr {
34                                ip: addr.ip()
35                            }),
36                        },
37                        None => request.remote().map(|addr| ClientRealAddr {
38                            ip: addr.ip()
39                        }),
40                    }
41                },
42                None => request.remote().map(|addr| ClientRealAddr {
43                    ip: addr.ip()
44                }),
45            }
46        },
47    }
48}
49
50#[rocket::async_trait]
51impl<'r> FromRequest<'r> for ClientRealAddr {
52    type Error = ();
53
54    async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
55        match from_request(request) {
56            Some(client_addr) => Outcome::Success(client_addr),
57            None => Outcome::Forward(Status::BadRequest),
58        }
59    }
60}
61
62#[rocket::async_trait]
63impl<'r> FromRequest<'r> for &'r ClientRealAddr {
64    type Error = ();
65
66    async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
67        let cache: &Option<ClientRealAddr> = request.local_cache(|| from_request(request));
68
69        match cache.as_ref() {
70            Some(client_addr) => Outcome::Success(client_addr),
71            None => Outcome::Forward(Status::BadRequest),
72        }
73    }
74}
75
76impl ClientRealAddr {
77    /// Get an `Ipv4Addr` instance.
78    pub fn get_ipv4(&self) -> Option<Ipv4Addr> {
79        match &self.ip {
80            IpAddr::V4(ipv4) => Some(*ipv4),
81            IpAddr::V6(ipv6) => ipv6.to_ipv4(),
82        }
83    }
84
85    /// Get an IPv4 string.
86    pub fn get_ipv4_string(&self) -> Option<String> {
87        match &self.ip {
88            IpAddr::V4(ipv4) => Some(ipv4.to_string()),
89            IpAddr::V6(ipv6) => ipv6.to_ipv4().map(|ipv6| ipv6.to_string()),
90        }
91    }
92
93    /// Get an `Ipv6Addr` instance.
94    pub fn get_ipv6(&self) -> Ipv6Addr {
95        match &self.ip {
96            IpAddr::V4(ipv4) => ipv4.to_ipv6_mapped(),
97            IpAddr::V6(ipv6) => *ipv6,
98        }
99    }
100
101    /// Get an IPv6 string.
102    pub fn get_ipv6_string(&self) -> String {
103        match &self.ip {
104            IpAddr::V4(ipv4) => ipv4.to_ipv6_mapped().to_string(),
105            IpAddr::V6(ipv6) => ipv6.to_string(),
106        }
107    }
108}