1- //! This service resolves the peer IP from the request .
1+ //! This service resolves the remote client address .
22//!
33//! The peer IP is used to identify the peer in the tracker. It's the peer IP
44//! that is used in the `announce` responses (peer list). And it's also used to
1212//! X-Forwarded-For: 126.0.0.1 X-Forwarded-For: 126.0.0.1,126.0.0.2
1313//! ```
1414//!
15- //! This service returns two options for the peer IP:
15+ //! This `ClientIpSources` contains two options for the peer IP:
1616//!
1717//! ```text
1818//! right_most_x_forwarded_for = 126.0.0.2
1919//! connection_info_ip = 126.0.0.3
2020//! ```
2121//!
22- //! Depending on the tracker configuration .
22+ //! Which one to use depends on the `ReverseProxyMode` .
2323use std:: net:: { IpAddr , SocketAddr } ;
2424use std:: panic:: Location ;
2525
2626use serde:: { Deserialize , Serialize } ;
2727use thiserror:: Error ;
2828
29+ /// Resolves the client's real address considering proxy headers. Port is also
30+ /// included when available.
31+ ///
32+ /// # Errors
33+ ///
34+ /// This function returns an error if the IP address cannot be resolved.
35+ pub fn resolve_remote_client_addr (
36+ reverse_proxy_mode : & ReverseProxyMode ,
37+ client_ip_sources : & ClientIpSources ,
38+ ) -> Result < RemoteClientAddr , PeerIpResolutionError > {
39+ let ip = match reverse_proxy_mode {
40+ ReverseProxyMode :: Enabled => ResolvedIp :: FromXForwardedFor ( client_ip_sources. try_client_ip_from_proxy_header ( ) ?) ,
41+ ReverseProxyMode :: Disabled => ResolvedIp :: FromSocketAddr ( client_ip_sources. try_client_ip_from_connection_info ( ) ?) ,
42+ } ;
43+
44+ let port = client_ip_sources. client_port_from_connection_info ( ) ;
45+
46+ Ok ( RemoteClientAddr :: new ( ip, port) )
47+ }
48+
49+ /// This struct indicates whether the tracker is running on reverse proxy mode.
50+ #[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , Clone , Copy ) ]
51+ pub enum ReverseProxyMode {
52+ Enabled ,
53+ Disabled ,
54+ }
55+
56+ impl From < ReverseProxyMode > for bool {
57+ fn from ( reverse_proxy_mode : ReverseProxyMode ) -> Self {
58+ match reverse_proxy_mode {
59+ ReverseProxyMode :: Enabled => true ,
60+ ReverseProxyMode :: Disabled => false ,
61+ }
62+ }
63+ }
64+
65+ impl From < bool > for ReverseProxyMode {
66+ fn from ( reverse_proxy_mode : bool ) -> Self {
67+ if reverse_proxy_mode {
68+ ReverseProxyMode :: Enabled
69+ } else {
70+ ReverseProxyMode :: Disabled
71+ }
72+ }
73+ }
2974/// This struct contains the sources from which the peer IP can be obtained.
3075#[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , Clone ) ]
3176pub struct ClientIpSources {
@@ -36,6 +81,36 @@ pub struct ClientIpSources {
3681 pub connection_info_socket_address : Option < SocketAddr > ,
3782}
3883
84+ impl ClientIpSources {
85+ fn try_client_ip_from_connection_info ( & self ) -> Result < IpAddr , PeerIpResolutionError > {
86+ if let Some ( socket_addr) = self . connection_info_socket_address {
87+ Ok ( socket_addr. ip ( ) )
88+ } else {
89+ Err ( PeerIpResolutionError :: MissingClientIp {
90+ location : Location :: caller ( ) ,
91+ } )
92+ }
93+ }
94+
95+ fn try_client_ip_from_proxy_header ( & self ) -> Result < IpAddr , PeerIpResolutionError > {
96+ if let Some ( ip) = self . right_most_x_forwarded_for {
97+ Ok ( ip)
98+ } else {
99+ Err ( PeerIpResolutionError :: MissingRightMostXForwardedForIp {
100+ location : Location :: caller ( ) ,
101+ } )
102+ }
103+ }
104+
105+ fn client_port_from_connection_info ( & self ) -> Option < u16 > {
106+ if self . connection_info_socket_address . is_some ( ) {
107+ self . connection_info_socket_address . map ( |socket_addr| socket_addr. port ( ) )
108+ } else {
109+ None
110+ }
111+ }
112+ }
113+
39114/// The error that can occur when resolving the peer IP.
40115#[ derive( Error , Debug , Clone ) ]
41116pub enum PeerIpResolutionError {
@@ -54,120 +129,79 @@ pub enum PeerIpResolutionError {
54129 MissingClientIp { location : & ' static Location < ' static > } ,
55130}
56131
57- /// Resolves the peer IP from the request.
58- ///
59- /// Given the sources from which the peer IP can be obtained, this function
60- /// resolves the peer IP according to the tracker configuration.
61- ///
62- /// With the tracker running on reverse proxy mode:
63- ///
64- /// ```rust
65- /// use std::net::IpAddr;
66- /// use std::str::FromStr;
67- ///
68- /// use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::{invoke, ClientIpSources, PeerIpResolutionError};
69- ///
70- /// let on_reverse_proxy = true;
71- ///
72- /// let ip = invoke(
73- /// on_reverse_proxy,
74- /// &ClientIpSources {
75- /// right_most_x_forwarded_for: Some(IpAddr::from_str("203.0.113.195").unwrap()),
76- /// connection_info_socket_address: None,
77- /// },
78- /// )
79- /// .unwrap();
80- ///
81- /// assert_eq!(ip, IpAddr::from_str("203.0.113.195").unwrap());
82- /// ```
83- ///
84- /// With the tracker non running on reverse proxy mode:
85- ///
86- /// ```rust
87- /// use std::net::{IpAddr,Ipv4Addr,SocketAddr};
88- /// use std::str::FromStr;
89- ///
90- /// use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::{invoke, ClientIpSources, PeerIpResolutionError};
91- ///
92- /// let on_reverse_proxy = false;
93- ///
94- /// let ip = invoke(
95- /// on_reverse_proxy,
96- /// &ClientIpSources {
97- /// right_most_x_forwarded_for: None,
98- /// connection_info_socket_address: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 195)), 8080))
99- /// },
100- /// )
101- /// .unwrap();
102- ///
103- /// assert_eq!(ip, IpAddr::from_str("203.0.113.195").unwrap());
104- /// ```
105- ///
106- /// # Errors
107- ///
108- /// Will return an error if the peer IP cannot be obtained according to the configuration.
109- /// For example, if the IP is extracted from an HTTP header which is missing in the request.
110- pub fn invoke ( on_reverse_proxy : bool , client_ip_sources : & ClientIpSources ) -> Result < IpAddr , PeerIpResolutionError > {
111- if on_reverse_proxy {
112- resolve_peer_ip_on_reverse_proxy ( client_ip_sources)
113- } else {
114- resolve_peer_ip_without_reverse_proxy ( client_ip_sources)
115- }
132+ #[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , Clone , Copy ) ]
133+ pub struct RemoteClientAddr {
134+ ip : ResolvedIp ,
135+ port : Option < u16 > ,
116136}
117137
118- fn resolve_peer_ip_without_reverse_proxy ( remote_client_ip : & ClientIpSources ) -> Result < IpAddr , PeerIpResolutionError > {
119- if let Some ( socket_addr) = remote_client_ip. connection_info_socket_address {
120- Ok ( socket_addr. ip ( ) )
121- } else {
122- Err ( PeerIpResolutionError :: MissingClientIp {
123- location : Location :: caller ( ) ,
124- } )
138+ impl RemoteClientAddr {
139+ #[ must_use]
140+ pub fn new ( ip : ResolvedIp , port : Option < u16 > ) -> Self {
141+ Self { ip, port }
142+ }
143+
144+ #[ must_use]
145+ pub fn ip ( & self ) -> IpAddr {
146+ match self . ip {
147+ ResolvedIp :: FromSocketAddr ( ip) | ResolvedIp :: FromXForwardedFor ( ip) => ip,
148+ }
125149 }
126- }
127150
128- fn resolve_peer_ip_on_reverse_proxy ( remote_client_ip : & ClientIpSources ) -> Result < IpAddr , PeerIpResolutionError > {
129- if let Some ( ip) = remote_client_ip. right_most_x_forwarded_for {
130- Ok ( ip)
131- } else {
132- Err ( PeerIpResolutionError :: MissingRightMostXForwardedForIp {
133- location : Location :: caller ( ) ,
134- } )
151+ #[ must_use]
152+ pub fn port ( & self ) -> Option < u16 > {
153+ self . port
135154 }
136155}
137156
157+ /// This enum indicates the source of the resolved IP address.
158+ #[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , Clone , Copy ) ]
159+ pub enum ResolvedIp {
160+ FromXForwardedFor ( IpAddr ) ,
161+ FromSocketAddr ( IpAddr ) ,
162+ }
163+
138164#[ cfg( test) ]
139165mod tests {
140- use super :: invoke ;
166+ use super :: resolve_remote_client_addr ;
141167
142168 mod working_without_reverse_proxy {
143169 use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
144170 use std:: str:: FromStr ;
145171
146- use super :: invoke;
147- use crate :: v1:: services:: peer_ip_resolver:: { ClientIpSources , PeerIpResolutionError } ;
172+ use super :: resolve_remote_client_addr;
173+ use crate :: v1:: services:: peer_ip_resolver:: {
174+ ClientIpSources , PeerIpResolutionError , RemoteClientAddr , ResolvedIp , ReverseProxyMode ,
175+ } ;
148176
149177 #[ test]
150- fn it_should_get_the_peer_ip_from_the_connection_info ( ) {
151- let on_reverse_proxy = false ;
178+ fn it_should_get_the_remote_client_address_from_the_connection_info ( ) {
179+ let reverse_proxy_mode = ReverseProxyMode :: Disabled ;
152180
153- let ip = invoke (
154- on_reverse_proxy ,
181+ let ip = resolve_remote_client_addr (
182+ & reverse_proxy_mode ,
155183 & ClientIpSources {
156184 right_most_x_forwarded_for : None ,
157185 connection_info_socket_address : Some ( SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 203 , 0 , 113 , 195 ) ) , 8080 ) ) ,
158186 } ,
159187 )
160188 . unwrap ( ) ;
161189
162- assert_eq ! ( ip, IpAddr :: from_str( "203.0.113.195" ) . unwrap( ) ) ;
190+ assert_eq ! (
191+ ip,
192+ RemoteClientAddr :: new(
193+ ResolvedIp :: FromSocketAddr ( IpAddr :: from_str( "203.0.113.195" ) . unwrap( ) ) ,
194+ Some ( 8080 )
195+ )
196+ ) ;
163197 }
164198
165199 #[ test]
166- fn it_should_return_an_error_if_it_cannot_get_the_peer_ip_from_the_connection_info ( ) {
167- let on_reverse_proxy = false ;
200+ fn it_should_return_an_error_if_it_cannot_get_the_remote_client_ip_from_the_connection_info ( ) {
201+ let reverse_proxy_mode = ReverseProxyMode :: Disabled ;
168202
169- let error = invoke (
170- on_reverse_proxy ,
203+ let error = resolve_remote_client_addr (
204+ & reverse_proxy_mode ,
171205 & ClientIpSources {
172206 right_most_x_forwarded_for : None ,
173207 connection_info_socket_address : None ,
@@ -179,34 +213,42 @@ mod tests {
179213 }
180214 }
181215
182- mod working_on_reverse_proxy {
216+ mod working_on_reverse_proxy_mode {
183217 use std:: net:: IpAddr ;
184218 use std:: str:: FromStr ;
185219
186- use crate :: v1:: services:: peer_ip_resolver:: { invoke, ClientIpSources , PeerIpResolutionError } ;
220+ use crate :: v1:: services:: peer_ip_resolver:: {
221+ resolve_remote_client_addr, ClientIpSources , PeerIpResolutionError , RemoteClientAddr , ResolvedIp , ReverseProxyMode ,
222+ } ;
187223
188224 #[ test]
189- fn it_should_get_the_peer_ip_from_the_right_most_ip_in_the_x_forwarded_for_header ( ) {
190- let on_reverse_proxy = true ;
225+ fn it_should_get_the_remote_client_ip_from_the_right_most_ip_in_the_x_forwarded_for_header ( ) {
226+ let reverse_proxy_mode = ReverseProxyMode :: Enabled ;
191227
192- let ip = invoke (
193- on_reverse_proxy ,
228+ let ip = resolve_remote_client_addr (
229+ & reverse_proxy_mode ,
194230 & ClientIpSources {
195231 right_most_x_forwarded_for : Some ( IpAddr :: from_str ( "203.0.113.195" ) . unwrap ( ) ) ,
196232 connection_info_socket_address : None ,
197233 } ,
198234 )
199235 . unwrap ( ) ;
200236
201- assert_eq ! ( ip, IpAddr :: from_str( "203.0.113.195" ) . unwrap( ) ) ;
237+ assert_eq ! (
238+ ip,
239+ RemoteClientAddr :: new(
240+ ResolvedIp :: FromXForwardedFor ( IpAddr :: from_str( "203.0.113.195" ) . unwrap( ) ) ,
241+ None
242+ )
243+ ) ;
202244 }
203245
204246 #[ test]
205247 fn it_should_return_an_error_if_it_cannot_get_the_right_most_ip_from_the_x_forwarded_for_header ( ) {
206- let on_reverse_proxy = true ;
248+ let reverse_proxy_mode = ReverseProxyMode :: Enabled ;
207249
208- let error = invoke (
209- on_reverse_proxy ,
250+ let error = resolve_remote_client_addr (
251+ & reverse_proxy_mode ,
210252 & ClientIpSources {
211253 right_most_x_forwarded_for : None ,
212254 connection_info_socket_address : None ,
0 commit comments