diff --git a/packages/axum-http-tracker-server/src/v1/extractors/announce_request.rs b/packages/axum-http-tracker-server/src/v1/extractors/announce_request.rs index 57001a47e..c2cc6c910 100644 --- a/packages/axum-http-tracker-server/src/v1/extractors/announce_request.rs +++ b/packages/axum-http-tracker-server/src/v1/extractors/announce_request.rs @@ -118,6 +118,7 @@ mod tests { event: Some(Event::Completed), compact: Some(Compact::NotAccepted), numwant: Some(50), + ip: None, } ); } diff --git a/packages/axum-http-tracker-server/src/v1/handlers/announce.rs b/packages/axum-http-tracker-server/src/v1/handlers/announce.rs index 16ff83f81..e5b7f9af5 100644 --- a/packages/axum-http-tracker-server/src/v1/handlers/announce.rs +++ b/packages/axum-http-tracker-server/src/v1/handlers/announce.rs @@ -200,6 +200,7 @@ mod tests { event: None, compact: None, numwant: None, + ip: None, } } diff --git a/packages/http-protocol/src/v1/requests/announce.rs b/packages/http-protocol/src/v1/requests/announce.rs index a04738749..ec961e161 100644 --- a/packages/http-protocol/src/v1/requests/announce.rs +++ b/packages/http-protocol/src/v1/requests/announce.rs @@ -16,6 +16,7 @@ use torrust_tracker_primitives::peer; use crate::percent_encoding::{percent_decode_info_hash, percent_decode_peer_id}; use crate::v1::query::{ParseQueryError, Query}; use crate::v1::responses; +use crate::v1::services::peer_ip_resolver::ResolvedIp; use crate::CurrentClock; // Query param names @@ -28,6 +29,7 @@ const LEFT: &str = "left"; const EVENT: &str = "event"; const COMPACT: &str = "compact"; const NUMWANT: &str = "numwant"; +const IP: &str = "ip"; /// The `Announce` request. Fields use the domain types after parsing the /// query params of the request. @@ -36,6 +38,9 @@ const NUMWANT: &str = "numwant"; /// use aquatic_udp_protocol::{NumberOfBytes, PeerId}; /// use bittorrent_http_tracker_protocol::v1::requests::announce::{Announce, Compact, Event}; /// use bittorrent_primitives::info_hash::InfoHash; +/// use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::ResolvedIp; +/// use std::net::IpAddr; +/// use std::str::FromStr; /// /// let request = Announce { /// // Mandatory params @@ -48,7 +53,8 @@ const NUMWANT: &str = "numwant"; /// left: Some(NumberOfBytes::new(1)), /// event: Some(Event::Started), /// compact: Some(Compact::NotAccepted), -/// numwant: Some(50) +/// numwant: Some(50), +/// ip: Some(ResolvedIp::FromSocketAddr(IpAddr::from_str("127.0.0.1").unwrap())), /// }; /// ``` /// @@ -91,6 +97,10 @@ pub struct Announce { /// Number of peers that the client would receive from the tracker. The /// value is permitted to be zero. pub numwant: Option, + + /// The IP address of the peer. This is an optional parameter. + /// When present, it indicates the source of the IP address (either from socket address or X-Forwarded-For header). + pub ip: Option, } /// Errors that can occur when parsing the `Announce` request. @@ -286,6 +296,7 @@ impl TryFrom for Announce { event: extract_event(&query)?, compact: extract_compact(&query)?, numwant: extract_numwant(&query)?, + ip: extract_ip(&query)?, }) } } @@ -406,6 +417,20 @@ fn extract_numwant(query: &Query) -> Result, ParseAnnounceQueryError } } +fn extract_ip(query: &Query) -> Result, ParseAnnounceQueryError> { + match query.get_param(IP) { + Some(raw_param) => { + let ip = IpAddr::from_str(&raw_param).map_err(|_| ParseAnnounceQueryError::InvalidParam { + param_name: IP.to_owned(), + param_value: raw_param.clone(), + location: Location::caller(), + })?; + Ok(Some(ResolvedIp::FromSocketAddr(ip))) + } + None => Ok(None), + } +} + /// It builds a `Peer` from the announce request. /// /// It ignores the peer address in the announce request params. @@ -427,16 +452,18 @@ pub fn peer_from_request(announce_request: &Announce, peer_ip: &IpAddr) -> peer: #[cfg(test)] mod tests { - mod announce_request { + use std::net::IpAddr; + use std::str::FromStr; use aquatic_udp_protocol::{NumberOfBytes, PeerId}; use bittorrent_primitives::info_hash::InfoHash; use crate::v1::query::Query; use crate::v1::requests::announce::{ - Announce, Compact, Event, COMPACT, DOWNLOADED, EVENT, INFO_HASH, LEFT, NUMWANT, PEER_ID, PORT, UPLOADED, + Announce, Compact, Event, COMPACT, DOWNLOADED, EVENT, INFO_HASH, IP, LEFT, NUMWANT, PEER_ID, PORT, UPLOADED, }; + use crate::v1::services::peer_ip_resolver::ResolvedIp; #[test] fn should_be_instantiated_from_the_url_query_with_only_the_mandatory_params() { @@ -463,6 +490,7 @@ mod tests { event: None, compact: None, numwant: None, + ip: None, } ); } @@ -479,6 +507,7 @@ mod tests { (EVENT, "started"), (COMPACT, "0"), (NUMWANT, "50"), + (IP, "127.0.0.1"), ]) .to_string(); @@ -498,6 +527,7 @@ mod tests { event: Some(Event::Started), compact: Some(Compact::NotAccepted), numwant: Some(50), + ip: Some(ResolvedIp::FromSocketAddr(IpAddr::from_str("127.0.0.1").unwrap())), } ); } diff --git a/packages/http-tracker-core/benches/helpers/util.rs b/packages/http-tracker-core/benches/helpers/util.rs index 414d3b40e..a72ba35b0 100644 --- a/packages/http-tracker-core/benches/helpers/util.rs +++ b/packages/http-tracker-core/benches/helpers/util.rs @@ -106,6 +106,7 @@ pub fn sample_announce_request_for_peer(peer: Peer) -> (Announce, ClientIpSource event: Some(peer.event.into()), compact: None, numwant: None, + ip: None, }; let client_ip_sources = ClientIpSources { diff --git a/packages/http-tracker-core/src/services/announce.rs b/packages/http-tracker-core/src/services/announce.rs index 23d589bce..cd9aa2d46 100644 --- a/packages/http-tracker-core/src/services/announce.rs +++ b/packages/http-tracker-core/src/services/announce.rs @@ -79,7 +79,11 @@ impl AnnounceService { self.authorize(announce_request.info_hash).await?; - let remote_client_addr = resolve_remote_client_addr(&self.core_config.net.on_reverse_proxy.into(), client_ip_sources)?; + let remote_client_addr = if let Some(ip) = announce_request.ip { + RemoteClientAddr::new(ip, Some(announce_request.port)) + } else { + resolve_remote_client_addr(&self.core_config.net.on_reverse_proxy.into(), client_ip_sources)? + }; let mut peer = peer_from_request(announce_request, &remote_client_addr.ip()); @@ -288,6 +292,7 @@ mod tests { event: Some(peer.event.into()), compact: None, numwant: None, + ip: None, }; let client_ip_sources = ClientIpSources {