Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axum HTTP tracker: announce request in listed mode #202

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/http/axum_implementation/handlers/announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub async fn handle_without_key(
) -> Response {
debug!("http announce request: {:#?}", announce_request);

if tracker.is_private() {
if tracker.requires_authentication() {
return responses::error::Error::from(auth::Error::MissingAuthKey {
location: Location::caller(),
})
Expand All @@ -47,6 +47,7 @@ pub async fn handle_with_key(
) -> Response {
debug!("http announce request: {:#?}", announce_request);

// todo: extract to Axum extractor. Duplicate code in `scrape` handler.
let Ok(key_id) = key_id_param.value().parse::<KeyId>() else {
return responses::error::Error::from(
auth::Error::InvalidKeyFormat {
Expand All @@ -55,7 +56,7 @@ pub async fn handle_with_key(
.into_response()
};

match auth::authenticate(&key_id, &tracker).await {
match tracker.authenticate(&key_id).await {
Ok(_) => (),
Err(error) => return responses::error::Error::from(error).into_response(),
}
Expand All @@ -64,6 +65,11 @@ pub async fn handle_with_key(
}

async fn handle(tracker: &Arc<Tracker>, announce_request: &Announce, remote_client_ip: &RemoteClientIp) -> Response {
match tracker.authorize(&announce_request.info_hash).await {
Ok(_) => (),
Err(error) => return responses::error::Error::from(error).into_response(),
}

let peer_ip = match peer_ip::resolve(tracker.config.on_reverse_proxy, remote_client_ip) {
Ok(peer_ip) => peer_ip,
Err(err) => return err,
Expand Down
15 changes: 1 addition & 14 deletions src/http/axum_implementation/handlers/auth.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::panic::Location;
use std::sync::Arc;

use serde::Deserialize;
use thiserror::Error;

use crate::http::axum_implementation::responses;
use crate::tracker::auth::{self, KeyId};
use crate::tracker::Tracker;
use crate::tracker::auth;

#[derive(Deserialize)]
pub struct KeyIdParam(String);
Expand All @@ -26,17 +24,6 @@ pub enum Error {
InvalidKeyFormat { location: &'static Location<'static> },
}

/// # Errors
///
/// Will return an error if the the authentication key cannot be verified.
pub async fn authenticate(key_id: &KeyId, tracker: &Arc<Tracker>) -> Result<(), auth::Error> {
if tracker.is_private() {
tracker.verify_auth_key(key_id).await
} else {
Ok(())
}
}

impl From<Error> for responses::error::Error {
fn from(err: Error) -> Self {
responses::error::Error {
Expand Down
11 changes: 11 additions & 0 deletions src/http/axum_implementation/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
use super::responses;
use crate::tracker::error::Error;

pub mod announce;
pub mod auth;
pub mod scrape;
pub mod status;

impl From<Error> for responses::error::Error {
fn from(err: Error) -> Self {
responses::error::Error {
failure_reason: format!("Tracker error: {err}"),
}
}
}
5 changes: 3 additions & 2 deletions src/http/axum_implementation/handlers/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub async fn handle_without_key(
) -> Response {
debug!("http scrape request: {:#?}", &scrape_request);

if tracker.is_private() {
if tracker.requires_authentication() {
return handle_fake_scrape(&tracker, &scrape_request, &remote_client_ip).await;
}

Expand All @@ -39,6 +39,7 @@ pub async fn handle_with_key(
) -> Response {
debug!("http scrape request: {:#?}", &scrape_request);

// todo: extract to Axum extractor. Duplicate code in `announce` handler.
let Ok(key_id) = key_id_param.value().parse::<KeyId>() else {
return responses::error::Error::from(
auth::Error::InvalidKeyFormat {
Expand All @@ -47,7 +48,7 @@ pub async fn handle_with_key(
.into_response()
};

match auth::authenticate(&key_id, &tracker).await {
match tracker.authenticate(&key_id).await {
Ok(_) => (),
Err(_) => return handle_fake_scrape(&tracker, &scrape_request, &remote_client_ip).await,
}
Expand Down
36 changes: 36 additions & 0 deletions src/tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ impl Tracker {
self.mode == mode::Mode::Listed || self.mode == mode::Mode::PrivateListed
}

pub fn requires_authentication(&self) -> bool {
self.is_private()
}

/// It handles an announce request.
///
/// BEP 03: [The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html).
Expand Down Expand Up @@ -334,6 +338,38 @@ impl Tracker {
Ok(())
}

/// # Errors
///
/// Will return an error if the the authentication key cannot be verified.
pub async fn authenticate(&self, key_id: &KeyId) -> Result<(), auth::Error> {
if self.is_private() {
self.verify_auth_key(key_id).await
} else {
Ok(())
}
}

/// The only authorization process is the whitelist.
///
/// # Errors
///
/// Will return an error if the tracker is running in `listed` mode
/// and the infohash is not whitelisted.
pub async fn authorize(&self, info_hash: &InfoHash) -> Result<(), Error> {
if !self.is_whitelisted() {
return Ok(());
}

if self.is_info_hash_whitelisted(info_hash).await {
return Ok(());
}

return Err(Error::TorrentNotWhitelisted {
info_hash: *info_hash,
location: Location::caller(),
});
}

/// Loading the torrents from database into memory
///
/// # Errors
Expand Down
6 changes: 2 additions & 4 deletions tests/http_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2403,8 +2403,7 @@ mod axum_http_tracker_server {
use crate::http::requests::announce::QueryBuilder;
use crate::http::server::start_whitelisted_http_tracker;

//#[tokio::test]
#[allow(dead_code)]
#[tokio::test]
async fn should_fail_if_the_torrent_is_not_in_the_whitelist() {
let http_tracker_server = start_whitelisted_http_tracker(Version::Axum).await;

Expand All @@ -2417,8 +2416,7 @@ mod axum_http_tracker_server {
assert_torrent_not_in_whitelist_error_response(response).await;
}

//#[tokio::test]
#[allow(dead_code)]
#[tokio::test]
async fn should_allow_announcing_a_whitelisted_torrent() {
let http_tracker_server = start_whitelisted_http_tracker(Version::Axum).await;

Expand Down