From f6bf07050cb2c48a4af459f1143de33fb19fbb0e Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Mon, 17 Feb 2025 19:04:31 +0000 Subject: [PATCH 1/7] refactor: [#1275] move announce authentication in http tracker to core --- .../http-protocol/src/v1/responses/error.rs | 8 +++++ .../src/authentication/key/mod.rs | 4 +++ .../http_tracker_core/services/announce.rs | 24 +++++++++++++- .../http/v1/extractors/authentication_key.rs | 10 +++--- src/servers/http/v1/handlers/announce.rs | 31 ++++--------------- src/servers/http/v1/handlers/common/auth.rs | 7 ++--- tests/servers/http/asserts.rs | 6 +++- 7 files changed, 53 insertions(+), 37 deletions(-) diff --git a/packages/http-protocol/src/v1/responses/error.rs b/packages/http-protocol/src/v1/responses/error.rs index 2bd8cd95c..30749f73a 100644 --- a/packages/http-protocol/src/v1/responses/error.rs +++ b/packages/http-protocol/src/v1/responses/error.rs @@ -79,6 +79,14 @@ impl From for Error { } } +impl From for Error { + fn from(err: bittorrent_tracker_core::authentication::Error) -> Self { + Error { + failure_reason: format!("Tracker authentication error: {err}"), + } + } +} + #[cfg(test)] mod tests { diff --git a/packages/tracker-core/src/authentication/key/mod.rs b/packages/tracker-core/src/authentication/key/mod.rs index 648143928..efc734356 100644 --- a/packages/tracker-core/src/authentication/key/mod.rs +++ b/packages/tracker-core/src/authentication/key/mod.rs @@ -185,6 +185,10 @@ pub enum Error { /// Indicates that the key has expired. #[error("Key has expired, {location}")] KeyExpired { location: &'static Location<'static> }, + + /// Indicates that the required key for authentication was not provided. + #[error("Missing authentication key, {location}")] + MissingAuthKey { location: &'static Location<'static> }, } impl From for Error { diff --git a/src/packages/http_tracker_core/services/announce.rs b/src/packages/http_tracker_core/services/announce.rs index 2bc421f1d..6c9cbec17 100644 --- a/src/packages/http_tracker_core/services/announce.rs +++ b/src/packages/http_tracker_core/services/announce.rs @@ -8,6 +8,7 @@ //! It also sends an [`http_tracker_core::statistics::event::Event`] //! because events are specific for the HTTP tracker. use std::net::IpAddr; +use std::panic::Location; use std::sync::Arc; use bittorrent_http_protocol::v1::requests::announce::{peer_from_request, Announce}; @@ -15,6 +16,7 @@ use bittorrent_http_protocol::v1::responses; use bittorrent_http_protocol::v1::services::peer_ip_resolver::{self, ClientIpSources}; use bittorrent_tracker_core::announce_handler::{AnnounceHandler, PeersWanted}; use bittorrent_tracker_core::authentication::service::AuthenticationService; +use bittorrent_tracker_core::authentication::{self, Key}; use bittorrent_tracker_core::whitelist; use torrust_tracker_configuration::Core; use torrust_tracker_primitives::core::AnnounceData; @@ -42,12 +44,28 @@ use crate::packages::http_tracker_core; pub async fn handle_announce( core_config: &Arc, announce_handler: &Arc, - _authentication_service: &Arc, + authentication_service: &Arc, whitelist_authorization: &Arc, opt_http_stats_event_sender: &Arc>>, announce_request: &Announce, client_ip_sources: &ClientIpSources, + maybe_key: Option, ) -> Result { + // Authentication + if core_config.private { + match maybe_key { + Some(key) => match authentication_service.authenticate(&key).await { + Ok(()) => (), + Err(error) => return Err(error.into()), + }, + None => { + return Err(responses::error::Error::from(authentication::key::Error::MissingAuthKey { + location: Location::caller(), + })) + } + } + } + // Authorization match whitelist_authorization.authorize(&announce_request.info_hash).await { Ok(()) => (), @@ -257,6 +275,7 @@ mod tests { &core_http_tracker_services.http_stats_event_sender, &announce_request, &client_ip_sources, + None, ) .await .unwrap(); @@ -300,6 +319,7 @@ mod tests { &core_http_tracker_services.http_stats_event_sender, &announce_request, &client_ip_sources, + None, ) .await .unwrap(); @@ -351,6 +371,7 @@ mod tests { &core_http_tracker_services.http_stats_event_sender, &announce_request, &client_ip_sources, + None, ) .await .unwrap(); @@ -383,6 +404,7 @@ mod tests { &core_http_tracker_services.http_stats_event_sender, &announce_request, &client_ip_sources, + None, ) .await .unwrap(); diff --git a/src/servers/http/v1/extractors/authentication_key.rs b/src/servers/http/v1/extractors/authentication_key.rs index 0e46b75dd..c99c7000a 100644 --- a/src/servers/http/v1/extractors/authentication_key.rs +++ b/src/servers/http/v1/extractors/authentication_key.rs @@ -117,11 +117,6 @@ fn custom_error(rejection: &PathRejection) -> responses::error::Error { location: Location::caller(), }) } - axum::extract::rejection::PathRejection::MissingPathParams(_) => { - responses::error::Error::from(auth::Error::MissingAuthKey { - location: Location::caller(), - }) - } _ => responses::error::Error::from(auth::Error::CannotExtractKeyParam { location: Location::caller(), }), @@ -148,6 +143,9 @@ mod tests { let response = parse_key(invalid_key).unwrap_err(); - assert_error_response(&response, "Authentication error: Invalid format for authentication key param"); + assert_error_response( + &response, + "Tracker authentication error: Invalid format for authentication key param", + ); } } diff --git a/src/servers/http/v1/handlers/announce.rs b/src/servers/http/v1/handlers/announce.rs index 5f25c317b..76f4e5134 100644 --- a/src/servers/http/v1/handlers/announce.rs +++ b/src/servers/http/v1/handlers/announce.rs @@ -5,7 +5,6 @@ //! //! The handlers perform the authentication and authorization of the request, //! and resolve the client IP address. -use std::panic::Location; use std::sync::Arc; use aquatic_udp_protocol::AnnounceEvent; @@ -22,12 +21,10 @@ use hyper::StatusCode; use torrust_tracker_configuration::Core; use torrust_tracker_primitives::core::AnnounceData; -use super::common::auth::map_auth_error_to_error_response; use crate::packages::http_tracker_core; use crate::servers::http::v1::extractors::announce_request::ExtractRequest; use crate::servers::http::v1::extractors::authentication_key::Extract as ExtractKey; use crate::servers::http::v1::extractors::client_ip_sources::Extract as ExtractClientIpSources; -use crate::servers::http::v1::handlers::common::auth; /// It handles the `announce` request when the HTTP tracker does not require /// authentication (no PATH `key` parameter required). @@ -134,23 +131,6 @@ async fn handle_announce( client_ip_sources: &ClientIpSources, maybe_key: Option, ) -> Result { - // todo: move authentication inside `http_tracker_core::services::announce::handle_announce` - - // Authentication - if core_config.private { - match maybe_key { - Some(key) => match authentication_service.authenticate(&key).await { - Ok(()) => (), - Err(error) => return Err(map_auth_error_to_error_response(&error)), - }, - None => { - return Err(responses::error::Error::from(auth::Error::MissingAuthKey { - location: Location::caller(), - })) - } - } - } - http_tracker_core::services::announce::handle_announce( &core_config.clone(), &announce_handler.clone(), @@ -159,6 +139,7 @@ async fn handle_announce( &opt_http_stats_event_sender.clone(), announce_request, client_ip_sources, + maybe_key, ) .await } @@ -339,10 +320,7 @@ mod tests { .await .unwrap_err(); - assert_error_response( - &response, - "Authentication error: Missing authentication key param for private tracker", - ); + assert_error_response(&response, "Tracker authentication error: Missing authentication key"); } #[tokio::test] @@ -366,7 +344,10 @@ mod tests { .await .unwrap_err(); - assert_error_response(&response, "Authentication error: Failed to read key"); + assert_error_response( + &response, + "Tracker authentication error: Failed to read key: YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ", + ); } } diff --git a/src/servers/http/v1/handlers/common/auth.rs b/src/servers/http/v1/handlers/common/auth.rs index c8625d03a..f45064ae3 100644 --- a/src/servers/http/v1/handlers/common/auth.rs +++ b/src/servers/http/v1/handlers/common/auth.rs @@ -14,10 +14,9 @@ use thiserror::Error; /// from the URL path. #[derive(Debug, Error)] pub enum Error { - #[error("Missing authentication key param for private tracker. Error in {location}")] - MissingAuthKey { location: &'static Location<'static> }, #[error("Invalid format for authentication key param. Error in {location}")] InvalidKeyFormat { location: &'static Location<'static> }, + #[error("Cannot extract authentication key param from URL path. Error in {location}")] CannotExtractKeyParam { location: &'static Location<'static> }, } @@ -25,7 +24,7 @@ pub enum Error { impl From for responses::error::Error { fn from(err: Error) -> Self { responses::error::Error { - failure_reason: format!("Authentication error: {err}"), + failure_reason: format!("Tracker authentication error: {err}"), } } } @@ -36,6 +35,6 @@ pub fn map_auth_error_to_error_response(err: &authentication::Error) -> response // impl From for responses::error::Error // Consider moving the trait implementation to the http-protocol package. responses::error::Error { - failure_reason: format!("Authentication error: {err}"), + failure_reason: format!("Tracker authentication error: {err}"), } } diff --git a/tests/servers/http/asserts.rs b/tests/servers/http/asserts.rs index 8d40d7e74..a68a1896e 100644 --- a/tests/servers/http/asserts.rs +++ b/tests/servers/http/asserts.rs @@ -141,5 +141,9 @@ pub async fn assert_cannot_parse_query_params_error_response(response: Response, pub async fn assert_authentication_error_response(response: Response) { assert_eq!(response.status(), 200); - assert_bencoded_error(&response.text().await.unwrap(), "Authentication error", Location::caller()); + assert_bencoded_error( + &response.text().await.unwrap(), + "Tracker authentication error", + Location::caller(), + ); } From ecc093f8f77889580177b5e8db4e6bfdab0ed594 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Mon, 17 Feb 2025 19:23:13 +0000 Subject: [PATCH 2/7] refactor: [#1275] move scrape authentication in http tracker to core --- .../http_tracker_core/services/scrape.rs | 88 ++++++++++++------- src/servers/http/v1/handlers/scrape.rs | 19 +--- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/packages/http_tracker_core/services/scrape.rs b/src/packages/http_tracker_core/services/scrape.rs index 667ce8d0d..7e3ea47fd 100644 --- a/src/packages/http_tracker_core/services/scrape.rs +++ b/src/packages/http_tracker_core/services/scrape.rs @@ -14,6 +14,8 @@ use bittorrent_http_protocol::v1::requests::scrape::Scrape; use bittorrent_http_protocol::v1::responses; use bittorrent_http_protocol::v1::services::peer_ip_resolver::{self, ClientIpSources}; use bittorrent_primitives::info_hash::InfoHash; +use bittorrent_tracker_core::authentication::service::AuthenticationService; +use bittorrent_tracker_core::authentication::Key; use bittorrent_tracker_core::scrape_handler::ScrapeHandler; use torrust_tracker_configuration::Core; use torrust_tracker_primitives::core::ScrapeData; @@ -40,12 +42,26 @@ use crate::packages::http_tracker_core; pub async fn handle_scrape( core_config: &Arc, scrape_handler: &Arc, + authentication_service: &Arc, opt_http_stats_event_sender: &Arc>>, scrape_request: &Scrape, client_ip_sources: &ClientIpSources, - return_fake_scrape_data: bool, + maybe_key: Option, ) -> Result { - // Authorization for scrape requests is handled at the `bittorrent-_racker_core` + // Authentication + let return_fake_scrape_data = if core_config.private { + match maybe_key { + Some(key) => match authentication_service.authenticate(&key).await { + Ok(()) => false, + Err(_error) => true, + }, + None => true, + } + } else { + false + }; + + // Authorization for scrape requests is handled at the `bittorrent_tracker_core` // level for each torrent. let peer_ip = match peer_ip_resolver::invoke(core_config.net.on_reverse_proxy, client_ip_sources) { @@ -111,6 +127,8 @@ mod tests { use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId}; use bittorrent_primitives::info_hash::InfoHash; use bittorrent_tracker_core::announce_handler::AnnounceHandler; + use bittorrent_tracker_core::authentication::key::repository::in_memory::InMemoryKeyRepository; + use bittorrent_tracker_core::authentication::service::AuthenticationService; use bittorrent_tracker_core::databases::setup::initialize_database; use bittorrent_tracker_core::scrape_handler::ScrapeHandler; use bittorrent_tracker_core::torrent::repository::in_memory::InMemoryTorrentRepository; @@ -127,27 +145,39 @@ mod tests { use crate::packages::http_tracker_core; use crate::servers::http::test_helpers::tests::sample_info_hash; - fn initialize_announce_and_scrape_handlers_for_public_tracker() -> (Arc, Arc) { - initialize_announce_and_scrape_handlers_with_configuration(&configuration::ephemeral_public()) + struct Container { + announce_handler: Arc, + scrape_handler: Arc, + authentication_service: Arc, } - fn initialize_announce_and_scrape_handlers_with_configuration( - config: &Configuration, - ) -> (Arc, Arc) { + fn initialize_services_for_public_tracker() -> Container { + initialize_services_with_configuration(&configuration::ephemeral_public()) + } + + fn initialize_services_with_configuration(config: &Configuration) -> Container { let database = initialize_database(&config.core); let in_memory_whitelist = Arc::new(InMemoryWhitelist::default()); let whitelist_authorization = Arc::new(WhitelistAuthorization::new(&config.core, &in_memory_whitelist.clone())); let in_memory_torrent_repository = Arc::new(InMemoryTorrentRepository::default()); let db_torrent_repository = Arc::new(DatabasePersistentTorrentRepository::new(&database)); + let in_memory_key_repository = Arc::new(InMemoryKeyRepository::default()); + let authentication_service = Arc::new(AuthenticationService::new(&config.core, &in_memory_key_repository)); + let announce_handler = Arc::new(AnnounceHandler::new( &config.core, &whitelist_authorization, &in_memory_torrent_repository, &db_torrent_repository, )); + let scrape_handler = Arc::new(ScrapeHandler::new(&whitelist_authorization, &in_memory_torrent_repository)); - (announce_handler, scrape_handler) + Container { + announce_handler, + scrape_handler, + authentication_service, + } } fn sample_info_hashes() -> Vec { @@ -166,14 +196,6 @@ mod tests { } } - fn initialize_scrape_handler_with_config(config: &Configuration) -> Arc { - let in_memory_whitelist = Arc::new(InMemoryWhitelist::default()); - let whitelist_authorization = Arc::new(WhitelistAuthorization::new(&config.core, &in_memory_whitelist.clone())); - let in_memory_torrent_repository = Arc::new(InMemoryTorrentRepository::default()); - - Arc::new(ScrapeHandler::new(&whitelist_authorization, &in_memory_torrent_repository)) - } - mock! { HttpStatsEventSender {} impl http_tracker_core::statistics::event::sender::Sender for HttpStatsEventSender { @@ -197,8 +219,7 @@ mod tests { use crate::packages::http_tracker_core::services::scrape::handle_scrape; use crate::packages::http_tracker_core::services::scrape::tests::{ - initialize_announce_and_scrape_handlers_with_configuration, initialize_scrape_handler_with_config, - sample_info_hashes, sample_peer, MockHttpStatsEventSender, + initialize_services_with_configuration, sample_info_hashes, sample_peer, MockHttpStatsEventSender, }; use crate::packages::{self, http_tracker_core}; use crate::servers::http::test_helpers::tests::sample_info_hash; @@ -212,7 +233,7 @@ mod tests { packages::http_tracker_core::statistics::setup::factory(false); let http_stats_event_sender = Arc::new(http_stats_event_sender); - let (announce_handler, scrape_handler) = initialize_announce_and_scrape_handlers_with_configuration(&configuration); + let container = initialize_services_with_configuration(&configuration); let info_hash = sample_info_hash(); let info_hashes = vec![info_hash]; @@ -220,7 +241,8 @@ mod tests { // Announce a new peer to force scrape data to contain non zeroed data let mut peer = sample_peer(); let original_peer_ip = peer.ip(); - announce_handler + container + .announce_handler .announce(&info_hash, &mut peer, &original_peer_ip, &PeersWanted::AsManyAsPossible) .await .unwrap(); @@ -236,11 +258,12 @@ mod tests { let scrape_data = handle_scrape( &core_config, - &scrape_handler, + &container.scrape_handler, + &container.authentication_service, &http_stats_event_sender, &scrape_request, &client_ip_sources, - false, + None, ) .await .unwrap(); @@ -271,7 +294,7 @@ mod tests { let http_stats_event_sender: Arc>> = Arc::new(Some(Box::new(http_stats_event_sender_mock))); - let scrape_handler = initialize_scrape_handler_with_config(&config); + let container = initialize_services_with_configuration(&config); let peer_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)); @@ -286,11 +309,12 @@ mod tests { handle_scrape( &Arc::new(config.core), - &scrape_handler, + &container.scrape_handler, + &container.authentication_service, &http_stats_event_sender, &scrape_request, &client_ip_sources, - false, + None, ) .await .unwrap(); @@ -309,7 +333,7 @@ mod tests { let http_stats_event_sender: Arc>> = Arc::new(Some(Box::new(http_stats_event_sender_mock))); - let scrape_handler = initialize_scrape_handler_with_config(&config); + let container = initialize_services_with_configuration(&config); let peer_ip = IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)); @@ -324,11 +348,12 @@ mod tests { handle_scrape( &Arc::new(config.core), - &scrape_handler, + &container.scrape_handler, + &container.authentication_service, &http_stats_event_sender, &scrape_request, &client_ip_sources, - false, + None, ) .await .unwrap(); @@ -347,7 +372,7 @@ mod tests { use crate::packages::http_tracker_core::services::scrape::fake; use crate::packages::http_tracker_core::services::scrape::tests::{ - initialize_announce_and_scrape_handlers_for_public_tracker, sample_info_hashes, sample_peer, MockHttpStatsEventSender, + initialize_services_for_public_tracker, sample_info_hashes, sample_peer, MockHttpStatsEventSender, }; use crate::packages::{self, http_tracker_core}; use crate::servers::http::test_helpers::tests::sample_info_hash; @@ -358,7 +383,7 @@ mod tests { packages::http_tracker_core::statistics::setup::factory(false); let http_stats_event_sender = Arc::new(http_stats_event_sender); - let (announce_handler, _scrape_handler) = initialize_announce_and_scrape_handlers_for_public_tracker(); + let container = initialize_services_for_public_tracker(); let info_hash = sample_info_hash(); let info_hashes = vec![info_hash]; @@ -366,7 +391,8 @@ mod tests { // Announce a new peer to force scrape data to contain not zeroed data let mut peer = sample_peer(); let original_peer_ip = peer.ip(); - announce_handler + container + .announce_handler .announce(&info_hash, &mut peer, &original_peer_ip, &PeersWanted::AsManyAsPossible) .await .unwrap(); diff --git a/src/servers/http/v1/handlers/scrape.rs b/src/servers/http/v1/handlers/scrape.rs index 2d41c2f78..946190e8f 100644 --- a/src/servers/http/v1/handlers/scrape.rs +++ b/src/servers/http/v1/handlers/scrape.rs @@ -107,6 +107,7 @@ async fn handle( Ok(scrape_data) => scrape_data, Err(error) => return (StatusCode::OK, error.write()).into_response(), }; + build_response(scrape_data) } @@ -120,28 +121,14 @@ async fn handle_scrape( client_ip_sources: &ClientIpSources, maybe_key: Option, ) -> Result { - // todo: move authentication inside `http_tracker_core::services::scrape::handle_scrape` - - // Authentication - let return_fake_scrape_data = if core_config.private { - match maybe_key { - Some(key) => match authentication_service.authenticate(&key).await { - Ok(()) => false, - Err(_error) => true, - }, - None => true, - } - } else { - false - }; - http_tracker_core::services::scrape::handle_scrape( core_config, scrape_handler, + authentication_service, opt_http_stats_event_sender, scrape_request, client_ip_sources, - return_fake_scrape_data, + maybe_key, ) .await } From 694621bc63ae542afca395e376a31008b60c43fd Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 18 Feb 2025 07:53:54 +0000 Subject: [PATCH 3/7] refactor: [#1275] extract ConnectionCookieError enum --- src/servers/udp/connection_cookie.rs | 18 ++++++++-------- src/servers/udp/error.rs | 32 +++++++++++++++++++++------- src/servers/udp/handlers/announce.rs | 2 +- src/servers/udp/handlers/mod.rs | 13 ++++------- src/servers/udp/handlers/scrape.rs | 2 +- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/servers/udp/connection_cookie.rs b/src/servers/udp/connection_cookie.rs index 439be9da7..5d8976f0e 100644 --- a/src/servers/udp/connection_cookie.rs +++ b/src/servers/udp/connection_cookie.rs @@ -82,7 +82,7 @@ use cookie_builder::{assemble, decode, disassemble, encode}; use tracing::instrument; use zerocopy::AsBytes; -use super::error::Error; +use crate::servers::udp::error::ConnectionCookieError; use crate::shared::crypto::keys::CipherArrayBlowfish; /// Generates a new connection cookie. @@ -96,9 +96,9 @@ use crate::shared::crypto::keys::CipherArrayBlowfish; /// It would panic if the cookie is not exactly 8 bytes is size. /// #[instrument(err)] -pub fn make(fingerprint: u64, issue_at: f64) -> Result { +pub fn make(fingerprint: u64, issue_at: f64) -> Result { if !issue_at.is_normal() { - return Err(Error::CookieValueNotNormal { + return Err(ConnectionCookieError::ValueNotNormal { not_normal_value: issue_at, }); } @@ -122,7 +122,7 @@ use std::ops::Range; /// /// It would panic if the range start is not smaller than it's end. #[instrument] -pub fn check(cookie: &Cookie, fingerprint: u64, valid_range: Range) -> Result { +pub fn check(cookie: &Cookie, fingerprint: u64, valid_range: Range) -> Result { assert!(valid_range.start <= valid_range.end, "range start is larger than range end"); let cookie_bytes = CipherArrayBlowfish::from_slice(cookie.0.as_bytes()); @@ -131,20 +131,20 @@ pub fn check(cookie: &Cookie, fingerprint: u64, valid_range: Range) -> Resu let issue_time = disassemble(fingerprint, cookie_bytes); if !issue_time.is_normal() { - return Err(Error::CookieValueNotNormal { + return Err(ConnectionCookieError::ValueNotNormal { not_normal_value: issue_time, }); } if issue_time < valid_range.start { - return Err(Error::CookieValueExpired { + return Err(ConnectionCookieError::ValueExpired { expired_value: issue_time, min_value: valid_range.start, }); } if issue_time > valid_range.end { - return Err(Error::CookieValueFromFuture { + return Err(ConnectionCookieError::ValueFromFuture { future_value: issue_time, max_value: valid_range.end, }); @@ -287,7 +287,7 @@ mod tests { let result = check(&cookie, fingerprint, min..max).unwrap_err(); match result { - Error::CookieValueExpired { .. } => {} // Expected error + ConnectionCookieError::ValueExpired { .. } => {} // Expected error _ => panic!("Expected ConnectionIdExpired error"), } } @@ -305,7 +305,7 @@ mod tests { let result = check(&cookie, fingerprint, min..max).unwrap_err(); match result { - Error::CookieValueFromFuture { .. } => {} // Expected error + ConnectionCookieError::ValueFromFuture { .. } => {} // Expected error _ => panic!("Expected ConnectionIdFromFuture error"), } } diff --git a/src/servers/udp/error.rs b/src/servers/udp/error.rs index cda562aed..6ba62a5e0 100644 --- a/src/servers/udp/error.rs +++ b/src/servers/udp/error.rs @@ -13,14 +13,9 @@ pub struct ConnectionCookie(pub ConnectionId); /// Error returned by the UDP server. #[derive(Error, Debug)] pub enum Error { - #[error("cookie value is not normal: {not_normal_value}")] - CookieValueNotNormal { not_normal_value: f64 }, - - #[error("cookie value is expired: {expired_value}, expected > {min_value}")] - CookieValueExpired { expired_value: f64, min_value: f64 }, - - #[error("cookie value is from future: {future_value}, expected < {max_value}")] - CookieValueFromFuture { future_value: f64, max_value: f64 }, + /// Error returned when there was an error with the connection cookie. + #[error("Connection cookie error: {source}")] + ConnectionCookieError { source: ConnectionCookieError }, #[error("error when phrasing request: {request_parse_error:?}")] RequestParseError { request_parse_error: RequestParseError }, @@ -49,8 +44,29 @@ pub enum Error { TrackerAuthenticationRequired { location: &'static Location<'static> }, } +/// Error returned when there was an error with the connection cookie. +#[derive(Error, Debug)] +pub enum ConnectionCookieError { + #[error("cookie value is not normal: {not_normal_value}")] + ValueNotNormal { not_normal_value: f64 }, + + #[error("cookie value is expired: {expired_value}, expected > {min_value}")] + ValueExpired { expired_value: f64, min_value: f64 }, + + #[error("cookie value is from future: {future_value}, expected < {max_value}")] + ValueFromFuture { future_value: f64, max_value: f64 }, +} + impl From for Error { fn from(request_parse_error: RequestParseError) -> Self { Self::RequestParseError { request_parse_error } } } + +impl From for Error { + fn from(connection_cookie_error: ConnectionCookieError) -> Self { + Self::ConnectionCookieError { + source: connection_cookie_error, + } + } +} diff --git a/src/servers/udp/handlers/announce.rs b/src/servers/udp/handlers/announce.rs index a273a2ecb..e5ee6dccf 100644 --- a/src/servers/udp/handlers/announce.rs +++ b/src/servers/udp/handlers/announce.rs @@ -50,7 +50,7 @@ pub async fn handle_announce( gen_remote_fingerprint(&remote_addr), cookie_valid_range, ) - .map_err(|e| (e, request.transaction_id))?; + .map_err(|e| (e.into(), request.transaction_id))?; let response = udp_tracker_core::services::announce::handle_announce( remote_addr, diff --git a/src/servers/udp/handlers/mod.rs b/src/servers/udp/handlers/mod.rs index f9f8edae7..2611931e3 100644 --- a/src/servers/udp/handlers/mod.rs +++ b/src/servers/udp/handlers/mod.rs @@ -78,15 +78,10 @@ pub(crate) async fn handle_packet( { Ok(response) => return response, Err((e, transaction_id)) => { - match &e { - Error::CookieValueNotNormal { .. } - | Error::CookieValueExpired { .. } - | Error::CookieValueFromFuture { .. } => { - // code-review: should we include `RequestParseError` and `BadRequest`? - let mut ban_service = udp_tracker_container.ban_service.write().await; - ban_service.increase_counter(&udp_request.from.ip()); - } - _ => {} + if let Error::ConnectionCookieError { .. } = &e { + // code-review: should we include `RequestParseError` and `BadRequest`? + let mut ban_service = udp_tracker_container.ban_service.write().await; + ban_service.increase_counter(&udp_request.from.ip()); } handle_error( diff --git a/src/servers/udp/handlers/scrape.rs b/src/servers/udp/handlers/scrape.rs index 2b03e0dc7..51a914e9f 100644 --- a/src/servers/udp/handlers/scrape.rs +++ b/src/servers/udp/handlers/scrape.rs @@ -42,7 +42,7 @@ pub async fn handle_scrape( gen_remote_fingerprint(&remote_addr), cookie_valid_range, ) - .map_err(|e| (e, request.transaction_id))?; + .map_err(|e| (e.into(), request.transaction_id))?; let scrape_data = udp_tracker_core::services::scrape::handle_scrape(remote_addr, request, scrape_handler, opt_udp_stats_event_sender) From 4fd79b798d8ef456444a02621dd348c7e3a6ac79 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 18 Feb 2025 08:03:56 +0000 Subject: [PATCH 4/7] refactor: [#1275] move conenction cookie to udp_tracker_core package --- .../udp_tracker_core}/connection_cookie.rs | 15 ++++++++++++++- src/packages/udp_tracker_core/mod.rs | 1 + src/servers/udp/error.rs | 15 ++------------- src/servers/udp/handlers/announce.rs | 12 ++++++------ src/servers/udp/handlers/connect.rs | 4 ++-- src/servers/udp/handlers/error.rs | 2 +- src/servers/udp/handlers/scrape.rs | 4 ++-- src/servers/udp/mod.rs | 1 - 8 files changed, 28 insertions(+), 26 deletions(-) rename src/{servers/udp => packages/udp_tracker_core}/connection_cookie.rs (95%) diff --git a/src/servers/udp/connection_cookie.rs b/src/packages/udp_tracker_core/connection_cookie.rs similarity index 95% rename from src/servers/udp/connection_cookie.rs rename to src/packages/udp_tracker_core/connection_cookie.rs index 5d8976f0e..2d8eae335 100644 --- a/src/servers/udp/connection_cookie.rs +++ b/src/packages/udp_tracker_core/connection_cookie.rs @@ -79,12 +79,25 @@ use aquatic_udp_protocol::ConnectionId as Cookie; use cookie_builder::{assemble, decode, disassemble, encode}; +use thiserror::Error; use tracing::instrument; use zerocopy::AsBytes; -use crate::servers::udp::error::ConnectionCookieError; use crate::shared::crypto::keys::CipherArrayBlowfish; +/// Error returned when there was an error with the connection cookie. +#[derive(Error, Debug)] +pub enum ConnectionCookieError { + #[error("cookie value is not normal: {not_normal_value}")] + ValueNotNormal { not_normal_value: f64 }, + + #[error("cookie value is expired: {expired_value}, expected > {min_value}")] + ValueExpired { expired_value: f64, min_value: f64 }, + + #[error("cookie value is from future: {future_value}, expected < {max_value}")] + ValueFromFuture { future_value: f64, max_value: f64 }, +} + /// Generates a new connection cookie. /// /// # Errors diff --git a/src/packages/udp_tracker_core/mod.rs b/src/packages/udp_tracker_core/mod.rs index 4f3e54857..1c93f811a 100644 --- a/src/packages/udp_tracker_core/mod.rs +++ b/src/packages/udp_tracker_core/mod.rs @@ -1,2 +1,3 @@ +pub mod connection_cookie; pub mod services; pub mod statistics; diff --git a/src/servers/udp/error.rs b/src/servers/udp/error.rs index 6ba62a5e0..81e7847c0 100644 --- a/src/servers/udp/error.rs +++ b/src/servers/udp/error.rs @@ -6,6 +6,8 @@ use derive_more::derive::Display; use thiserror::Error; use torrust_tracker_located_error::LocatedError; +use crate::packages::udp_tracker_core::connection_cookie::ConnectionCookieError; + #[derive(Display, Debug)] #[display(":?")] pub struct ConnectionCookie(pub ConnectionId); @@ -44,19 +46,6 @@ pub enum Error { TrackerAuthenticationRequired { location: &'static Location<'static> }, } -/// Error returned when there was an error with the connection cookie. -#[derive(Error, Debug)] -pub enum ConnectionCookieError { - #[error("cookie value is not normal: {not_normal_value}")] - ValueNotNormal { not_normal_value: f64 }, - - #[error("cookie value is expired: {expired_value}, expected > {min_value}")] - ValueExpired { expired_value: f64, min_value: f64 }, - - #[error("cookie value is from future: {future_value}, expected < {max_value}")] - ValueFromFuture { future_value: f64, max_value: f64 }, -} - impl From for Error { fn from(request_parse_error: RequestParseError) -> Self { Self::RequestParseError { request_parse_error } diff --git a/src/servers/udp/handlers/announce.rs b/src/servers/udp/handlers/announce.rs index e5ee6dccf..c9dd15735 100644 --- a/src/servers/udp/handlers/announce.rs +++ b/src/servers/udp/handlers/announce.rs @@ -14,8 +14,8 @@ use torrust_tracker_configuration::Core; use tracing::{instrument, Level}; use zerocopy::network_endian::I32; +use crate::packages::udp_tracker_core::connection_cookie::check; use crate::packages::udp_tracker_core::{self}; -use crate::servers::udp::connection_cookie::check; use crate::servers::udp::error::Error; use crate::servers::udp::handlers::gen_remote_fingerprint; @@ -134,7 +134,7 @@ mod tests { PeerId as AquaticPeerId, PeerKey, Port, TransactionId, }; - use crate::servers::udp::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::servers::udp::handlers::tests::{sample_ipv4_remote_addr_fingerprint, sample_issue_time}; struct AnnounceRequestBuilder { @@ -213,8 +213,8 @@ mod tests { use mockall::predicate::eq; use torrust_tracker_configuration::Core; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::packages::{self, udp_tracker_core}; - use crate::servers::udp::connection_cookie::make; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_default_tracker_configuration, @@ -447,7 +447,7 @@ mod tests { use aquatic_udp_protocol::{InfoHash as AquaticInfoHash, PeerId as AquaticPeerId}; - use crate::servers::udp::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_issue_time, @@ -520,8 +520,8 @@ mod tests { use mockall::predicate::eq; use torrust_tracker_configuration::Core; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::packages::{self, udp_tracker_core}; - use crate::servers::udp::connection_cookie::make; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_default_tracker_configuration, @@ -776,7 +776,7 @@ mod tests { use mockall::predicate::eq; use crate::packages::udp_tracker_core; - use crate::servers::udp::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; use crate::servers::udp::handlers::tests::{ sample_cookie_valid_range, sample_issue_time, MockUdpStatsEventSender, TrackerConfigurationBuilder, diff --git a/src/servers/udp/handlers/connect.rs b/src/servers/udp/handlers/connect.rs index 431c3bb4d..799e46347 100644 --- a/src/servers/udp/handlers/connect.rs +++ b/src/servers/udp/handlers/connect.rs @@ -6,7 +6,7 @@ use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, Response}; use tracing::{instrument, Level}; use crate::packages::udp_tracker_core; -use crate::servers::udp::connection_cookie::make; +use crate::packages::udp_tracker_core::connection_cookie::make; use crate::servers::udp::handlers::gen_remote_fingerprint; /// It handles the `Connect` request. Refer to [`Connect`](crate::servers::udp#connect) @@ -62,8 +62,8 @@ mod tests { use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, Response, TransactionId}; use mockall::predicate::eq; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::packages::{self, udp_tracker_core}; - use crate::servers::udp::connection_cookie::make; use crate::servers::udp::handlers::handle_connect; use crate::servers::udp::handlers::tests::{ sample_ipv4_remote_addr, sample_ipv4_remote_addr_fingerprint, sample_ipv4_socket_address, sample_ipv6_remote_addr, diff --git a/src/servers/udp/handlers/error.rs b/src/servers/udp/handlers/error.rs index 36095eeed..e5529dbdf 100644 --- a/src/servers/udp/handlers/error.rs +++ b/src/servers/udp/handlers/error.rs @@ -9,7 +9,7 @@ use uuid::Uuid; use zerocopy::network_endian::I32; use crate::packages::udp_tracker_core; -use crate::servers::udp::connection_cookie::check; +use crate::packages::udp_tracker_core::connection_cookie::check; use crate::servers::udp::error::Error; use crate::servers::udp::handlers::gen_remote_fingerprint; use crate::servers::udp::UDP_TRACKER_LOG_TARGET; diff --git a/src/servers/udp/handlers/scrape.rs b/src/servers/udp/handlers/scrape.rs index 51a914e9f..9bc445417 100644 --- a/src/servers/udp/handlers/scrape.rs +++ b/src/servers/udp/handlers/scrape.rs @@ -11,7 +11,7 @@ use tracing::{instrument, Level}; use zerocopy::network_endian::I32; use crate::packages::udp_tracker_core; -use crate::servers::udp::connection_cookie::check; +use crate::packages::udp_tracker_core::connection_cookie::check; use crate::servers::udp::error::Error; use crate::servers::udp::handlers::gen_remote_fingerprint; @@ -94,7 +94,7 @@ mod tests { use bittorrent_tracker_core::torrent::repository::in_memory::InMemoryTorrentRepository; use crate::packages; - use crate::servers::udp::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::make; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_ipv4_remote_addr, sample_issue_time, TorrentPeerBuilder, diff --git a/src/servers/udp/mod.rs b/src/servers/udp/mod.rs index e8410e5f0..2f0d4e4ce 100644 --- a/src/servers/udp/mod.rs +++ b/src/servers/udp/mod.rs @@ -637,7 +637,6 @@ use std::net::SocketAddr; -pub mod connection_cookie; pub mod error; pub mod handlers; pub mod server; From 91525af0c8e6d8686187909fe8735a4a89345eb2 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 18 Feb 2025 09:09:10 +0000 Subject: [PATCH 5/7] refactor: [#1275] move authentication in udp tracker to core --- .../udp_tracker_core/connection_cookie.rs | 11 +++- .../udp_tracker_core/services/announce.rs | 52 ++++++++++++++++++- .../udp_tracker_core/services/scrape.rs | 51 +++++++++++++++++- src/servers/udp/error.rs | 36 ++++++++----- src/servers/udp/handlers/announce.rs | 33 ++++-------- src/servers/udp/handlers/connect.rs | 3 +- src/servers/udp/handlers/error.rs | 3 +- src/servers/udp/handlers/mod.rs | 20 +++---- src/servers/udp/handlers/scrape.rs | 25 +++------ src/servers/udp/mod.rs | 2 +- 10 files changed, 160 insertions(+), 76 deletions(-) diff --git a/src/packages/udp_tracker_core/connection_cookie.rs b/src/packages/udp_tracker_core/connection_cookie.rs index 2d8eae335..b9070c63a 100644 --- a/src/packages/udp_tracker_core/connection_cookie.rs +++ b/src/packages/udp_tracker_core/connection_cookie.rs @@ -86,7 +86,7 @@ use zerocopy::AsBytes; use crate::shared::crypto::keys::CipherArrayBlowfish; /// Error returned when there was an error with the connection cookie. -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum ConnectionCookieError { #[error("cookie value is not normal: {not_normal_value}")] ValueNotNormal { not_normal_value: f64 }, @@ -123,6 +123,8 @@ pub fn make(fingerprint: u64, issue_at: f64) -> Result) -> Resu Ok(issue_time) } +#[must_use] +pub(crate) fn gen_remote_fingerprint(remote_addr: &SocketAddr) -> u64 { + let mut state = DefaultHasher::new(); + remote_addr.hash(&mut state); + state.finish() +} + mod cookie_builder { use cipher::{BlockDecrypt, BlockEncrypt}; use tracing::instrument; diff --git a/src/packages/udp_tracker_core/services/announce.rs b/src/packages/udp_tracker_core/services/announce.rs index 5851cdc92..a825d06ad 100644 --- a/src/packages/udp_tracker_core/services/announce.rs +++ b/src/packages/udp_tracker_core/services/announce.rs @@ -8,19 +8,57 @@ //! It also sends an [`udp_tracker_core::statistics::event::Event`] //! because events are specific for the HTTP tracker. use std::net::{IpAddr, SocketAddr}; +use std::ops::Range; use std::sync::Arc; use aquatic_udp_protocol::AnnounceRequest; use bittorrent_primitives::info_hash::InfoHash; use bittorrent_tracker_core::announce_handler::{AnnounceHandler, PeersWanted}; -use bittorrent_tracker_core::error::AnnounceError; +use bittorrent_tracker_core::error::{AnnounceError, WhitelistError}; use bittorrent_tracker_core::whitelist; use bittorrent_udp_protocol::peer_builder; use torrust_tracker_primitives::core::AnnounceData; use torrust_tracker_primitives::peer; +use crate::packages::udp_tracker_core::connection_cookie::{check, gen_remote_fingerprint, ConnectionCookieError}; use crate::packages::udp_tracker_core::{self}; +/// Errors related to announce requests. +#[derive(thiserror::Error, Debug, Clone)] +pub enum UdpAnnounceError { + /// Error returned when there was an error with the connection cookie. + #[error("Connection cookie error: {source}")] + ConnectionCookieError { source: ConnectionCookieError }, + + /// Error returned when there was an error with the tracker core announce handler. + #[error("Tracker core announce error: {source}")] + TrackerCoreAnnounceError { source: AnnounceError }, + + /// Error returned when there was an error with the tracker core whitelist. + #[error("Tracker core whitelist error: {source}")] + TrackerCoreWhitelistError { source: WhitelistError }, +} + +impl From for UdpAnnounceError { + fn from(connection_cookie_error: ConnectionCookieError) -> Self { + Self::ConnectionCookieError { + source: connection_cookie_error, + } + } +} + +impl From for UdpAnnounceError { + fn from(announce_error: AnnounceError) -> Self { + Self::TrackerCoreAnnounceError { source: announce_error } + } +} + +impl From for UdpAnnounceError { + fn from(whitelist_error: WhitelistError) -> Self { + Self::TrackerCoreWhitelistError { source: whitelist_error } + } +} + /// It handles the `Announce` request. /// /// # Errors @@ -36,7 +74,17 @@ pub async fn handle_announce( announce_handler: &Arc, whitelist_authorization: &Arc, opt_udp_stats_event_sender: &Arc>>, -) -> Result { + cookie_valid_range: Range, +) -> Result { + // todo: return a UDP response like the HTTP tracker instead of raw AnnounceData. + + // Authentication + check( + &request.connection_id, + gen_remote_fingerprint(&remote_addr), + cookie_valid_range, + )?; + let info_hash = request.info_hash.into(); let remote_client_ip = remote_addr.ip(); diff --git a/src/packages/udp_tracker_core/services/scrape.rs b/src/packages/udp_tracker_core/services/scrape.rs index 07ae452f8..5beb54e9f 100644 --- a/src/packages/udp_tracker_core/services/scrape.rs +++ b/src/packages/udp_tracker_core/services/scrape.rs @@ -8,15 +8,53 @@ //! It also sends an [`udp_tracker_core::statistics::event::Event`] //! because events are specific for the UDP tracker. use std::net::SocketAddr; +use std::ops::Range; use std::sync::Arc; use aquatic_udp_protocol::ScrapeRequest; use bittorrent_primitives::info_hash::InfoHash; -use bittorrent_tracker_core::error::ScrapeError; +use bittorrent_tracker_core::error::{ScrapeError, WhitelistError}; use bittorrent_tracker_core::scrape_handler::ScrapeHandler; use torrust_tracker_primitives::core::ScrapeData; use crate::packages::udp_tracker_core; +use crate::packages::udp_tracker_core::connection_cookie::{check, gen_remote_fingerprint, ConnectionCookieError}; + +/// Errors related to scrape requests. +#[derive(thiserror::Error, Debug, Clone)] +pub enum UdpScrapeError { + /// Error returned when there was an error with the connection cookie. + #[error("Connection cookie error: {source}")] + ConnectionCookieError { source: ConnectionCookieError }, + + /// Error returned when there was an error with the tracker core scrape handler. + #[error("Tracker core scrape error: {source}")] + TrackerCoreScrapeError { source: ScrapeError }, + + /// Error returned when there was an error with the tracker core whitelist. + #[error("Tracker core whitelist error: {source}")] + TrackerCoreWhitelistError { source: WhitelistError }, +} + +impl From for UdpScrapeError { + fn from(connection_cookie_error: ConnectionCookieError) -> Self { + Self::ConnectionCookieError { + source: connection_cookie_error, + } + } +} + +impl From for UdpScrapeError { + fn from(scrape_error: ScrapeError) -> Self { + Self::TrackerCoreScrapeError { source: scrape_error } + } +} + +impl From for UdpScrapeError { + fn from(whitelist_error: WhitelistError) -> Self { + Self::TrackerCoreWhitelistError { source: whitelist_error } + } +} /// It handles the `Scrape` request. /// @@ -28,7 +66,16 @@ pub async fn handle_scrape( request: &ScrapeRequest, scrape_handler: &Arc, opt_udp_stats_event_sender: &Arc>>, -) -> Result { + cookie_valid_range: Range, +) -> Result { + // todo: return a UDP response like the HTTP tracker instead of raw ScrapeData. + + check( + &request.connection_id, + gen_remote_fingerprint(&remote_addr), + cookie_valid_range, + )?; + // Convert from aquatic infohashes let info_hashes: Vec = request.info_hashes.iter().map(|&x| x.into()).collect(); diff --git a/src/servers/udp/error.rs b/src/servers/udp/error.rs index 81e7847c0..9105ba0cb 100644 --- a/src/servers/udp/error.rs +++ b/src/servers/udp/error.rs @@ -6,7 +6,8 @@ use derive_more::derive::Display; use thiserror::Error; use torrust_tracker_located_error::LocatedError; -use crate::packages::udp_tracker_core::connection_cookie::ConnectionCookieError; +use crate::packages::udp_tracker_core::services::announce::UdpAnnounceError; +use crate::packages::udp_tracker_core::services::scrape::UdpScrapeError; #[derive(Display, Debug)] #[display(":?")] @@ -15,18 +16,17 @@ pub struct ConnectionCookie(pub ConnectionId); /// Error returned by the UDP server. #[derive(Error, Debug)] pub enum Error { - /// Error returned when there was an error with the connection cookie. - #[error("Connection cookie error: {source}")] - ConnectionCookieError { source: ConnectionCookieError }, - + /// Error returned when the request is invalid. #[error("error when phrasing request: {request_parse_error:?}")] RequestParseError { request_parse_error: RequestParseError }, - /// Error returned when the domain tracker returns an error. - #[error("tracker server error: {source}")] - TrackerError { - source: LocatedError<'static, dyn std::error::Error + Send + Sync>, - }, + /// Error returned when the domain tracker returns an announce error. + #[error("tracker announce error: {source}")] + UdpAnnounceError { source: UdpAnnounceError }, + + /// Error returned when the domain tracker returns an scrape error. + #[error("tracker scrape error: {source}")] + UdpScrapeError { source: UdpScrapeError }, /// Error returned from a third-party library (`aquatic_udp_protocol`). #[error("internal server error: {message}, {location}")] @@ -52,10 +52,18 @@ impl From for Error { } } -impl From for Error { - fn from(connection_cookie_error: ConnectionCookieError) -> Self { - Self::ConnectionCookieError { - source: connection_cookie_error, +impl From for Error { + fn from(udp_announce_error: UdpAnnounceError) -> Self { + Self::UdpAnnounceError { + source: udp_announce_error, + } + } +} + +impl From for Error { + fn from(udp_scrape_error: UdpScrapeError) -> Self { + Self::UdpScrapeError { + source: udp_scrape_error, } } } diff --git a/src/servers/udp/handlers/announce.rs b/src/servers/udp/handlers/announce.rs index c9dd15735..48e0d6179 100644 --- a/src/servers/udp/handlers/announce.rs +++ b/src/servers/udp/handlers/announce.rs @@ -14,10 +14,8 @@ use torrust_tracker_configuration::Core; use tracing::{instrument, Level}; use zerocopy::network_endian::I32; -use crate::packages::udp_tracker_core::connection_cookie::check; use crate::packages::udp_tracker_core::{self}; use crate::servers::udp::error::Error; -use crate::servers::udp::handlers::gen_remote_fingerprint; /// It handles the `Announce` request. Refer to [`Announce`](crate::servers::udp#announce) /// request for more information. @@ -43,27 +41,16 @@ pub async fn handle_announce( tracing::trace!("handle announce"); - // todo: move authentication to `udp_tracker_core::services::announce::handle_announce` - - check( - &request.connection_id, - gen_remote_fingerprint(&remote_addr), - cookie_valid_range, - ) - .map_err(|e| (e.into(), request.transaction_id))?; - let response = udp_tracker_core::services::announce::handle_announce( remote_addr, request, announce_handler, whitelist_authorization, opt_udp_stats_event_sender, + cookie_valid_range, ) .await - .map_err(|e| Error::TrackerError { - source: (Arc::new(e) as Arc).into(), - }) - .map_err(|e| (e, request.transaction_id))?; + .map_err(|e| (e.into(), request.transaction_id))?; // todo: extract `build_response` function. @@ -213,15 +200,15 @@ mod tests { use mockall::predicate::eq; use torrust_tracker_configuration::Core; - use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; use crate::packages::{self, udp_tracker_core}; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; + use crate::servers::udp::handlers::handle_announce; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_default_tracker_configuration, initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_ipv4_socket_address, sample_issue_time, MockUdpStatsEventSender, TorrentPeerBuilder, }; - use crate::servers::udp::handlers::{gen_remote_fingerprint, handle_announce}; #[tokio::test] async fn an_announced_peer_should_be_added_to_the_tracker() { @@ -447,13 +434,13 @@ mod tests { use aquatic_udp_protocol::{InfoHash as AquaticInfoHash, PeerId as AquaticPeerId}; - use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; + use crate::servers::udp::handlers::handle_announce; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_issue_time, TorrentPeerBuilder, }; - use crate::servers::udp::handlers::{gen_remote_fingerprint, handle_announce}; #[tokio::test] async fn the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration_if_defined() { @@ -520,15 +507,15 @@ mod tests { use mockall::predicate::eq; use torrust_tracker_configuration::Core; - use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; use crate::packages::{self, udp_tracker_core}; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; + use crate::servers::udp::handlers::handle_announce; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_default_tracker_configuration, initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_ipv6_remote_addr, sample_issue_time, MockUdpStatsEventSender, TorrentPeerBuilder, }; - use crate::servers::udp::handlers::{gen_remote_fingerprint, handle_announce}; #[tokio::test] async fn an_announced_peer_should_be_added_to_the_tracker() { @@ -776,12 +763,12 @@ mod tests { use mockall::predicate::eq; use crate::packages::udp_tracker_core; - use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; use crate::servers::udp::handlers::announce::tests::announce_request::AnnounceRequestBuilder; + use crate::servers::udp::handlers::handle_announce; use crate::servers::udp::handlers::tests::{ sample_cookie_valid_range, sample_issue_time, MockUdpStatsEventSender, TrackerConfigurationBuilder, }; - use crate::servers::udp::handlers::{gen_remote_fingerprint, handle_announce}; #[tokio::test] async fn the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration() { diff --git a/src/servers/udp/handlers/connect.rs b/src/servers/udp/handlers/connect.rs index 799e46347..bd3c4ef0a 100644 --- a/src/servers/udp/handlers/connect.rs +++ b/src/servers/udp/handlers/connect.rs @@ -6,8 +6,7 @@ use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, Response}; use tracing::{instrument, Level}; use crate::packages::udp_tracker_core; -use crate::packages::udp_tracker_core::connection_cookie::make; -use crate::servers::udp::handlers::gen_remote_fingerprint; +use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; /// It handles the `Connect` request. Refer to [`Connect`](crate::servers::udp#connect) /// request for more information. diff --git a/src/servers/udp/handlers/error.rs b/src/servers/udp/handlers/error.rs index e5529dbdf..6cf273e78 100644 --- a/src/servers/udp/handlers/error.rs +++ b/src/servers/udp/handlers/error.rs @@ -9,9 +9,8 @@ use uuid::Uuid; use zerocopy::network_endian::I32; use crate::packages::udp_tracker_core; -use crate::packages::udp_tracker_core::connection_cookie::check; +use crate::packages::udp_tracker_core::connection_cookie::{check, gen_remote_fingerprint}; use crate::servers::udp::error::Error; -use crate::servers::udp::handlers::gen_remote_fingerprint; use crate::servers::udp::UDP_TRACKER_LOG_TARGET; #[allow(clippy::too_many_arguments)] diff --git a/src/servers/udp/handlers/mod.rs b/src/servers/udp/handlers/mod.rs index 2611931e3..e58497d4b 100644 --- a/src/servers/udp/handlers/mod.rs +++ b/src/servers/udp/handlers/mod.rs @@ -4,7 +4,6 @@ pub mod connect; pub mod error; pub mod scrape; -use std::hash::{DefaultHasher, Hash, Hasher as _}; use std::net::SocketAddr; use std::ops::Range; use std::sync::Arc; @@ -21,6 +20,7 @@ use uuid::Uuid; use super::RawRequest; use crate::container::UdpTrackerContainer; +use crate::packages::udp_tracker_core::services::announce::UdpAnnounceError; use crate::servers::udp::error::Error; use crate::shared::bit_torrent::common::MAX_SCRAPE_TORRENTS; use crate::CurrentClock; @@ -77,8 +77,11 @@ pub(crate) async fn handle_packet( .await { Ok(response) => return response, - Err((e, transaction_id)) => { - if let Error::ConnectionCookieError { .. } = &e { + Err((error, transaction_id)) => { + if let Error::UdpAnnounceError { + source: UdpAnnounceError::ConnectionCookieError { .. }, + } = error + { // code-review: should we include `RequestParseError` and `BadRequest`? let mut ban_service = udp_tracker_container.ban_service.write().await; ban_service.increase_counter(&udp_request.from.ip()); @@ -90,7 +93,7 @@ pub(crate) async fn handle_packet( request_id, &udp_tracker_container.udp_stats_event_sender, cookie_time_values.valid_range.clone(), - &e, + &error, Some(transaction_id), ) .await @@ -163,13 +166,6 @@ pub async fn handle_request( } } -#[must_use] -pub(crate) fn gen_remote_fingerprint(remote_addr: &SocketAddr) -> u64 { - let mut state = DefaultHasher::new(); - remote_addr.hash(&mut state); - state.finish() -} - #[cfg(test)] pub(crate) mod tests { @@ -194,8 +190,8 @@ pub(crate) mod tests { use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch}; use torrust_tracker_test_helpers::configuration; - use super::gen_remote_fingerprint; use crate::packages::udp_tracker_core; + use crate::packages::udp_tracker_core::connection_cookie::gen_remote_fingerprint; use crate::{packages, CurrentClock}; pub(crate) struct CoreTrackerServices { diff --git a/src/servers/udp/handlers/scrape.rs b/src/servers/udp/handlers/scrape.rs index 9bc445417..bca284860 100644 --- a/src/servers/udp/handlers/scrape.rs +++ b/src/servers/udp/handlers/scrape.rs @@ -11,9 +11,7 @@ use tracing::{instrument, Level}; use zerocopy::network_endian::I32; use crate::packages::udp_tracker_core; -use crate::packages::udp_tracker_core::connection_cookie::check; use crate::servers::udp::error::Error; -use crate::servers::udp::handlers::gen_remote_fingerprint; /// It handles the `Scrape` request. Refer to [`Scrape`](crate::servers::udp#scrape) /// request for more information. @@ -35,23 +33,16 @@ pub async fn handle_scrape( tracing::trace!("handle scrape"); - // todo: move authentication to `udp_tracker_core::services::scrape::handle_scrape` - - check( - &request.connection_id, - gen_remote_fingerprint(&remote_addr), + let scrape_data = udp_tracker_core::services::scrape::handle_scrape( + remote_addr, + request, + scrape_handler, + opt_udp_stats_event_sender, cookie_valid_range, ) + .await .map_err(|e| (e.into(), request.transaction_id))?; - let scrape_data = - udp_tracker_core::services::scrape::handle_scrape(remote_addr, request, scrape_handler, opt_udp_stats_event_sender) - .await - .map_err(|e| Error::TrackerError { - source: (Arc::new(e) as Arc).into(), - }) - .map_err(|e| (e, request.transaction_id))?; - // todo: extract `build_response` function. let mut torrent_stats: Vec = Vec::new(); @@ -94,12 +85,12 @@ mod tests { use bittorrent_tracker_core::torrent::repository::in_memory::InMemoryTorrentRepository; use crate::packages; - use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; + use crate::servers::udp::handlers::handle_scrape; use crate::servers::udp::handlers::tests::{ initialize_core_tracker_services_for_public_tracker, sample_cookie_valid_range, sample_ipv4_remote_addr, sample_issue_time, TorrentPeerBuilder, }; - use crate::servers::udp::handlers::{gen_remote_fingerprint, handle_scrape}; fn zeroed_torrent_statistics() -> TorrentScrapeStatistics { TorrentScrapeStatistics { diff --git a/src/servers/udp/mod.rs b/src/servers/udp/mod.rs index 2f0d4e4ce..614db5bf6 100644 --- a/src/servers/udp/mod.rs +++ b/src/servers/udp/mod.rs @@ -105,7 +105,7 @@ //! connection ID = hash(client IP + current time slot + secret seed) //! ``` //! -//! The BEP-15 recommends a two-minute time slot. Refer to [`connection_cookie`] +//! The BEP-15 recommends a two-minute time slot. Refer to [`connection_cookie`](crate::packages::udp_tracker_core::connection_cookie) //! for more information about the connection ID generation with this method. //! //! #### Connect Request From 4618f706dba7c53291e3c7366fc4b1ec147d3524 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 18 Feb 2025 09:50:03 +0000 Subject: [PATCH 6/7] refactor: exatract response builders for UDP handlers --- src/servers/udp/handlers/announce.rs | 28 ++++++++++++++++++---------- src/servers/udp/handlers/connect.rs | 18 ++++++++++++------ src/servers/udp/handlers/scrape.rs | 7 +++++-- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/servers/udp/handlers/announce.rs b/src/servers/udp/handlers/announce.rs index 48e0d6179..1003b4041 100644 --- a/src/servers/udp/handlers/announce.rs +++ b/src/servers/udp/handlers/announce.rs @@ -11,6 +11,7 @@ use bittorrent_primitives::info_hash::InfoHash; use bittorrent_tracker_core::announce_handler::AnnounceHandler; use bittorrent_tracker_core::whitelist; use torrust_tracker_configuration::Core; +use torrust_tracker_primitives::core::AnnounceData; use tracing::{instrument, Level}; use zerocopy::network_endian::I32; @@ -41,7 +42,7 @@ pub async fn handle_announce( tracing::trace!("handle announce"); - let response = udp_tracker_core::services::announce::handle_announce( + let announce_data = udp_tracker_core::services::announce::handle_announce( remote_addr, request, announce_handler, @@ -52,18 +53,25 @@ pub async fn handle_announce( .await .map_err(|e| (e.into(), request.transaction_id))?; - // todo: extract `build_response` function. + Ok(build_response(remote_addr, request, core_config, &announce_data)) +} +fn build_response( + remote_addr: SocketAddr, + request: &AnnounceRequest, + core_config: &Arc, + announce_data: &AnnounceData, +) -> Response { #[allow(clippy::cast_possible_truncation)] if remote_addr.is_ipv4() { let announce_response = AnnounceResponse { fixed: AnnounceResponseFixedData { transaction_id: request.transaction_id, announce_interval: AnnounceInterval(I32::new(i64::from(core_config.announce_policy.interval) as i32)), - leechers: NumberOfPeers(I32::new(i64::from(response.stats.incomplete) as i32)), - seeders: NumberOfPeers(I32::new(i64::from(response.stats.complete) as i32)), + leechers: NumberOfPeers(I32::new(i64::from(announce_data.stats.incomplete) as i32)), + seeders: NumberOfPeers(I32::new(i64::from(announce_data.stats.complete) as i32)), }, - peers: response + peers: announce_data .peers .iter() .filter_map(|peer| { @@ -79,16 +87,16 @@ pub async fn handle_announce( .collect(), }; - Ok(Response::from(announce_response)) + Response::from(announce_response) } else { let announce_response = AnnounceResponse { fixed: AnnounceResponseFixedData { transaction_id: request.transaction_id, announce_interval: AnnounceInterval(I32::new(i64::from(core_config.announce_policy.interval) as i32)), - leechers: NumberOfPeers(I32::new(i64::from(response.stats.incomplete) as i32)), - seeders: NumberOfPeers(I32::new(i64::from(response.stats.complete) as i32)), + leechers: NumberOfPeers(I32::new(i64::from(announce_data.stats.incomplete) as i32)), + seeders: NumberOfPeers(I32::new(i64::from(announce_data.stats.complete) as i32)), }, - peers: response + peers: announce_data .peers .iter() .filter_map(|peer| { @@ -104,7 +112,7 @@ pub async fn handle_announce( .collect(), }; - Ok(Response::from(announce_response)) + Response::from(announce_response) } } diff --git a/src/servers/udp/handlers/connect.rs b/src/servers/udp/handlers/connect.rs index bd3c4ef0a..8275d36af 100644 --- a/src/servers/udp/handlers/connect.rs +++ b/src/servers/udp/handlers/connect.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use std::sync::Arc; -use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, Response}; +use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, ConnectionId, Response}; use tracing::{instrument, Level}; use crate::packages::udp_tracker_core; @@ -25,12 +25,9 @@ pub async fn handle_connect( tracing::trace!("handle connect"); - let connection_id = make(gen_remote_fingerprint(&remote_addr), cookie_issue_time).expect("it should be a normal value"); + // todo: move to connect service in udp_tracker_core - let response = ConnectResponse { - transaction_id: request.transaction_id, - connection_id, - }; + let connection_id = make(gen_remote_fingerprint(&remote_addr), cookie_issue_time).expect("it should be a normal value"); if let Some(udp_stats_event_sender) = opt_udp_stats_event_sender.as_deref() { match remote_addr { @@ -47,6 +44,15 @@ pub async fn handle_connect( } } + build_response(*request, connection_id) +} + +fn build_response(request: ConnectRequest, connection_id: ConnectionId) -> Response { + let response = ConnectResponse { + transaction_id: request.transaction_id, + connection_id, + }; + Response::from(response) } diff --git a/src/servers/udp/handlers/scrape.rs b/src/servers/udp/handlers/scrape.rs index bca284860..b36eb92a0 100644 --- a/src/servers/udp/handlers/scrape.rs +++ b/src/servers/udp/handlers/scrape.rs @@ -7,6 +7,7 @@ use aquatic_udp_protocol::{ NumberOfDownloads, NumberOfPeers, Response, ScrapeRequest, ScrapeResponse, TorrentScrapeStatistics, TransactionId, }; use bittorrent_tracker_core::scrape_handler::ScrapeHandler; +use torrust_tracker_primitives::core::ScrapeData; use tracing::{instrument, Level}; use zerocopy::network_endian::I32; @@ -43,8 +44,10 @@ pub async fn handle_scrape( .await .map_err(|e| (e.into(), request.transaction_id))?; - // todo: extract `build_response` function. + Ok(build_response(request, &scrape_data)) +} +fn build_response(request: &ScrapeRequest, scrape_data: &ScrapeData) -> Response { let mut torrent_stats: Vec = Vec::new(); for file in &scrape_data.files { @@ -67,7 +70,7 @@ pub async fn handle_scrape( torrent_stats, }; - Ok(Response::from(response)) + Response::from(response) } #[cfg(test)] From fdc2543f49691b7f5d81698f9c326cd94a78f1cc Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 18 Feb 2025 10:08:03 +0000 Subject: [PATCH 7/7] refactor: extract UDP connect service --- .../udp_tracker_core/services/connect.rs | 129 ++++++++++++++++++ src/packages/udp_tracker_core/services/mod.rs | 49 +++++++ src/servers/udp/handlers/connect.rs | 26 +--- 3 files changed, 180 insertions(+), 24 deletions(-) create mode 100644 src/packages/udp_tracker_core/services/connect.rs diff --git a/src/packages/udp_tracker_core/services/connect.rs b/src/packages/udp_tracker_core/services/connect.rs new file mode 100644 index 000000000..4cc8b0a3b --- /dev/null +++ b/src/packages/udp_tracker_core/services/connect.rs @@ -0,0 +1,129 @@ +//! The `connect` service. +//! +//! The service is responsible for handling the `connect` requests. +use std::net::SocketAddr; +use std::sync::Arc; + +use aquatic_udp_protocol::ConnectionId; + +use crate::packages::udp_tracker_core; +use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; + +/// # Panics +/// +/// IT will panic if there was an error making the connection cookie. +pub async fn handle_connect( + remote_addr: SocketAddr, + opt_udp_stats_event_sender: &Arc>>, + cookie_issue_time: f64, +) -> ConnectionId { + // todo: return a UDP response like the HTTP tracker instead of raw ConnectionId. + + let connection_id = make(gen_remote_fingerprint(&remote_addr), cookie_issue_time).expect("it should be a normal value"); + + if let Some(udp_stats_event_sender) = opt_udp_stats_event_sender.as_deref() { + match remote_addr { + SocketAddr::V4(_) => { + udp_stats_event_sender + .send_event(udp_tracker_core::statistics::event::Event::Udp4Connect) + .await; + } + SocketAddr::V6(_) => { + udp_stats_event_sender + .send_event(udp_tracker_core::statistics::event::Event::Udp6Connect) + .await; + } + } + } + + connection_id +} + +#[cfg(test)] +mod tests { + + mod connect_request { + + use std::future; + use std::sync::Arc; + + use mockall::predicate::eq; + + use crate::packages::udp_tracker_core::connection_cookie::make; + use crate::packages::udp_tracker_core::services::connect::handle_connect; + use crate::packages::udp_tracker_core::services::tests::{ + sample_ipv4_remote_addr, sample_ipv4_remote_addr_fingerprint, sample_ipv4_socket_address, sample_ipv6_remote_addr, + sample_ipv6_remote_addr_fingerprint, sample_issue_time, MockUdpStatsEventSender, + }; + use crate::packages::{self, udp_tracker_core}; + + #[tokio::test] + async fn a_connect_response_should_contain_the_same_transaction_id_as_the_connect_request() { + let (udp_stats_event_sender, _udp_stats_repository) = packages::udp_tracker_core::statistics::setup::factory(false); + let udp_stats_event_sender = Arc::new(udp_stats_event_sender); + + let response = handle_connect(sample_ipv4_remote_addr(), &udp_stats_event_sender, sample_issue_time()).await; + + assert_eq!( + response, + make(sample_ipv4_remote_addr_fingerprint(), sample_issue_time()).unwrap() + ); + } + + #[tokio::test] + async fn a_connect_response_should_contain_a_new_connection_id() { + let (udp_stats_event_sender, _udp_stats_repository) = packages::udp_tracker_core::statistics::setup::factory(false); + let udp_stats_event_sender = Arc::new(udp_stats_event_sender); + + let response = handle_connect(sample_ipv4_remote_addr(), &udp_stats_event_sender, sample_issue_time()).await; + + assert_eq!( + response, + make(sample_ipv4_remote_addr_fingerprint(), sample_issue_time()).unwrap(), + ); + } + + #[tokio::test] + async fn a_connect_response_should_contain_a_new_connection_id_ipv6() { + let (udp_stats_event_sender, _udp_stats_repository) = packages::udp_tracker_core::statistics::setup::factory(false); + let udp_stats_event_sender = Arc::new(udp_stats_event_sender); + + let response = handle_connect(sample_ipv6_remote_addr(), &udp_stats_event_sender, sample_issue_time()).await; + + assert_eq!( + response, + make(sample_ipv6_remote_addr_fingerprint(), sample_issue_time()).unwrap(), + ); + } + + #[tokio::test] + async fn it_should_send_the_upd4_connect_event_when_a_client_tries_to_connect_using_a_ip4_socket_address() { + let mut udp_stats_event_sender_mock = MockUdpStatsEventSender::new(); + udp_stats_event_sender_mock + .expect_send_event() + .with(eq(udp_tracker_core::statistics::event::Event::Udp4Connect)) + .times(1) + .returning(|_| Box::pin(future::ready(Some(Ok(()))))); + let udp_stats_event_sender: Arc>> = + Arc::new(Some(Box::new(udp_stats_event_sender_mock))); + + let client_socket_address = sample_ipv4_socket_address(); + + handle_connect(client_socket_address, &udp_stats_event_sender, sample_issue_time()).await; + } + + #[tokio::test] + async fn it_should_send_the_upd6_connect_event_when_a_client_tries_to_connect_using_a_ip6_socket_address() { + let mut udp_stats_event_sender_mock = MockUdpStatsEventSender::new(); + udp_stats_event_sender_mock + .expect_send_event() + .with(eq(udp_tracker_core::statistics::event::Event::Udp6Connect)) + .times(1) + .returning(|_| Box::pin(future::ready(Some(Ok(()))))); + let udp_stats_event_sender: Arc>> = + Arc::new(Some(Box::new(udp_stats_event_sender_mock))); + + handle_connect(sample_ipv6_remote_addr(), &udp_stats_event_sender, sample_issue_time()).await; + } + } +} diff --git a/src/packages/udp_tracker_core/services/mod.rs b/src/packages/udp_tracker_core/services/mod.rs index 776d2dfbf..5b222c4d9 100644 --- a/src/packages/udp_tracker_core/services/mod.rs +++ b/src/packages/udp_tracker_core/services/mod.rs @@ -1,2 +1,51 @@ pub mod announce; +pub mod connect; pub mod scrape; + +#[cfg(test)] +pub(crate) mod tests { + + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + + use futures::future::BoxFuture; + use mockall::mock; + use tokio::sync::mpsc::error::SendError; + + use crate::packages::udp_tracker_core; + use crate::packages::udp_tracker_core::connection_cookie::gen_remote_fingerprint; + + pub(crate) fn sample_ipv4_remote_addr() -> SocketAddr { + sample_ipv4_socket_address() + } + + pub(crate) fn sample_ipv4_remote_addr_fingerprint() -> u64 { + gen_remote_fingerprint(&sample_ipv4_socket_address()) + } + + pub(crate) fn sample_ipv6_remote_addr() -> SocketAddr { + sample_ipv6_socket_address() + } + + pub(crate) fn sample_ipv6_remote_addr_fingerprint() -> u64 { + gen_remote_fingerprint(&sample_ipv6_socket_address()) + } + + pub(crate) fn sample_ipv4_socket_address() -> SocketAddr { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080) + } + + fn sample_ipv6_socket_address() -> SocketAddr { + SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080) + } + + pub(crate) fn sample_issue_time() -> f64 { + 1_000_000_000_f64 + } + + mock! { + pub(crate) UdpStatsEventSender {} + impl udp_tracker_core::statistics::event::sender::Sender for UdpStatsEventSender { + fn send_event(&self, event: udp_tracker_core::statistics::event::Event) -> BoxFuture<'static,Option > > > ; + } + } +} diff --git a/src/servers/udp/handlers/connect.rs b/src/servers/udp/handlers/connect.rs index 8275d36af..aae6a25f5 100644 --- a/src/servers/udp/handlers/connect.rs +++ b/src/servers/udp/handlers/connect.rs @@ -6,14 +6,9 @@ use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, ConnectionId, Respon use tracing::{instrument, Level}; use crate::packages::udp_tracker_core; -use crate::packages::udp_tracker_core::connection_cookie::{gen_remote_fingerprint, make}; /// It handles the `Connect` request. Refer to [`Connect`](crate::servers::udp#connect) /// request for more information. -/// -/// # Errors -/// -/// This function does not ever return an error. #[instrument(fields(transaction_id), skip(opt_udp_stats_event_sender), ret(level = Level::TRACE))] pub async fn handle_connect( remote_addr: SocketAddr, @@ -22,27 +17,10 @@ pub async fn handle_connect( cookie_issue_time: f64, ) -> Response { tracing::Span::current().record("transaction_id", request.transaction_id.0.to_string()); - tracing::trace!("handle connect"); - // todo: move to connect service in udp_tracker_core - - let connection_id = make(gen_remote_fingerprint(&remote_addr), cookie_issue_time).expect("it should be a normal value"); - - if let Some(udp_stats_event_sender) = opt_udp_stats_event_sender.as_deref() { - match remote_addr { - SocketAddr::V4(_) => { - udp_stats_event_sender - .send_event(udp_tracker_core::statistics::event::Event::Udp4Connect) - .await; - } - SocketAddr::V6(_) => { - udp_stats_event_sender - .send_event(udp_tracker_core::statistics::event::Event::Udp6Connect) - .await; - } - } - } + let connection_id = + udp_tracker_core::services::connect::handle_connect(remote_addr, opt_udp_stats_event_sender, cookie_issue_time).await; build_response(*request, connection_id) }