Skip to content
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
5 changes: 5 additions & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"words": [
"appuser",
"Arvid",
"AUTOINCREMENT",
"automock",
"Avicora",
Expand All @@ -21,6 +22,7 @@
"chrono",
"clippy",
"completei",
"connectionless",
"dockerhub",
"downloadedi",
"filesd",
Expand All @@ -47,6 +49,7 @@
"nanos",
"nextest",
"nocapture",
"Norberg",
"numwant",
"oneshot",
"ostr",
Expand All @@ -59,6 +62,7 @@
"reqwest",
"rerequests",
"rngs",
"routable",
"rusqlite",
"rustfmt",
"Rustls",
Expand All @@ -82,6 +86,7 @@
"Vagaa",
"Vuze",
"whitespaces",
"XBTT",
"Xtorrent",
"Xunlei",
"xxxxxxxxxxxxxxxxxxxxd",
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@
//! - [BEP 15](https://www.bittorrent.org/beps/bep_0015.html): UDP Tracker Protocol for `BitTorrent`
//! - [BEP 23](https://www.bittorrent.org/beps/bep_0023.html): Tracker Returns Compact Peer Lists
//! - [BEP 27](https://www.bittorrent.org/beps/bep_0027.html): Private Torrents
//! - [BEP 41](https://www.bittorrent.org/beps/bep_0041.html): UDP Tracker Protocol Extensions
//! - [BEP 48](https://www.bittorrent.org/beps/bep_0048.html): Tracker Protocol Extension: Scrape
//!
//! # Contributing
Expand Down
4 changes: 2 additions & 2 deletions src/servers/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
//!
//! Parameter | Type | Description | Required | Default | Example
//! ---|---|---|---|---|---
//! [`info_hash`](crate::servers::http::v1::requests::announce::Announce::info_hash) | percent encoded of 40-byte array | The `Info Hash` of the torrent. | Yes | No | `%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00`
//! [`info_hash`](crate::servers::http::v1::requests::announce::Announce::info_hash) | percent encoded of 20-byte array | The `Info Hash` of the torrent. | Yes | No | `%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00`
//! `peer_addr` | string |The IP address of the peer. | No | No | `2.137.87.41`
//! [`downloaded`](crate::servers::http::v1::requests::announce::Announce::downloaded) | positive integer |The number of bytes downloaded by the peer. | No | `0` | `0`
//! [`uploaded`](crate::servers::http::v1::requests::announce::Announce::uploaded) | positive integer | The number of bytes uploaded by the peer. | No | `0` | `0`
Expand Down Expand Up @@ -220,7 +220,7 @@
//!
//! Parameter | Type | Description | Required | Default | Example
//! ---|---|---|---|---|---
//! [`info_hash`](crate::servers::http::v1::requests::scrape::Scrape::info_hashes) | percent encoded of 40-byte array | The `Info Hash` of the torrent. | Yes | No | `%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00`
//! [`info_hash`](crate::servers::http::v1::requests::scrape::Scrape::info_hashes) | percent encoded of 20-byte array | The `Info Hash` of the torrent. | Yes | No | `%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00`
//!
//! > **NOTICE**: you can scrape multiple torrents at the same time by passing
//! multiple `info_hash` parameters.
Expand Down
2 changes: 1 addition & 1 deletion src/servers/http/percent_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::shared::bit_torrent::info_hash::{ConversionError, InfoHash};
use crate::tracker::peer::{self, IdConversionError};

/// Percent decodes a percent encoded infohash. Internally an
/// [`InfoHash`](crate::shared::bit_torrent::info_hash::InfoHash) is a 40-byte array.
/// [`InfoHash`](crate::shared::bit_torrent::info_hash::InfoHash) is a 20-byte array.
///
/// For example, given the infohash `3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0`,
/// it's percent encoded representation is `%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0`.
Expand Down
8 changes: 5 additions & 3 deletions src/servers/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ pub enum Error {
Error(String), // todo: refactor to use thiserror and add more variants for specific errors.
}

/// A stopped HTTP server.
/// A HTTP server instance controller with no HTTP instance running.
#[allow(clippy::module_name_repetitions)]
pub type StoppedHttpServer<I> = HttpServer<Stopped<I>>;

/// A running HTTP server.
/// A HTTP server instance controller with a running HTTP instance.
#[allow(clippy::module_name_repetitions)]
pub type RunningHttpServer<I> = HttpServer<Running<I>>;

/// A HTTP running server controller.
/// A HTTP server instance controller.
///
/// It's responsible for:
///
Expand Down Expand Up @@ -83,12 +83,14 @@ pub struct Stopped<I: HttpServerLauncher> {

/// A running HTTP server state.
pub struct Running<I: HttpServerLauncher> {
/// The address where the server is bound.
pub bind_addr: SocketAddr,
task_killer: tokio::sync::oneshot::Sender<u8>,
task: tokio::task::JoinHandle<I>,
}

impl<I: HttpServerLauncher + 'static> HttpServer<Stopped<I>> {
/// It creates a new `HttpServer` controller in `stopped` state.
pub fn new(cfg: torrust_tracker_configuration::HttpTracker, launcher: I) -> Self {
Self {
cfg,
Expand Down
2 changes: 1 addition & 1 deletion src/servers/http/v1/requests/announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub struct Announce {
/// Errors that can occur when parsing the `Announce` request.
///
/// The `info_hash` and `peer_id` query params are special because they contain
/// binary data. The `info_hash` is a 40-byte SHA1 hash and the `peer_id` is a
/// binary data. The `info_hash` is a 20-byte SHA1 hash and the `peer_id` is a
/// 20-byte array.
#[derive(Error, Debug)]
pub enum ParseAnnounceQueryError {
Expand Down
2 changes: 1 addition & 1 deletion src/servers/signals.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// This module contains functions to handle signals.
//! This module contains functions to handle signals.
use log::info;

/// Resolves on `ctrl_c` or the `terminate` signal.
Expand Down
73 changes: 73 additions & 0 deletions src/servers/udp/connection_cookie.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
//! Logic for generating and verifying connection IDs.
//!
//! The UDP tracker requires the client to connect to the server before it can
//! send any data. The server responds with a random 64-bit integer that the
//! client must use to identify itself.
//!
//! This connection ID is used to avoid spoofing attacks. The client must send
//! the connection ID in all requests to the server. The server will ignore any
//! requests that do not contain the correct connection ID.
//!
//! The simplest way to implement this would be to generate a random number when
//! the client connects and store it in a hash table. However, this would
//! require the server to store a large number of connection IDs, which would be
//! a waste of memory. Instead, the server generates a connection ID based on
//! the client's IP address and the current time. This allows the server to
//! verify the connection ID without storing it.
//!
//! This module implements this method of generating connection IDs. It's the
//! most common way to generate connection IDs. The connection ID is generated
//! using a time based algorithm and it is valid for a certain amount of time
//! (usually two minutes). The connection ID is generated using the following:
//!
//! ```text
//! connection ID = hash(client IP + current time slot + secret seed)
//! ```
//!
//! Time slots are two minute intervals since the Unix epoch. The secret seed is
//! a random number that is generated when the server starts. And the client IP
//! is used in order generate a unique connection ID for each client.
//!
//! The BEP-15 recommends a two-minute time slot.
//!
//! ```text
//! Timestamp (seconds from Unix epoch):
//! |------------|------------|------------|------------|
//! 0 120 240 360 480
//! Time slots (two-minutes time extents from Unix epoch):
//! |------------|------------|------------|------------|
//! 0 1 2 3 4
//! Peer connections:
//! Peer A |-------------------------|
//! Peer B |-------------------------|
//! Peer C |------------------|
//! Peer A connects at timestamp 120 slot 1 -> connection ID will be valid from timestamp 120 to 360
//! Peer B connects at timestamp 240 slot 2 -> connection ID will be valid from timestamp 240 to 480
//! Peer C connects at timestamp 180 slot 1 -> connection ID will be valid from timestamp 180 to 360
//! ```
//! > **NOTICE**: connection ID is always the same for a given peer
//! (socket address) and time slot.
//!
//! > **NOTICE**: connection ID will be valid for two time extents, **not two
//! minutes**. It'll be valid for the the current time extent and the next one.
//!
//! Refer to [`Connect`](crate::servers::udp#connect) for more information about
//! the connection process.
//!
//! ## Advantages
//!
//! - It consumes less memory than storing a hash table of connection IDs.
//! - It's easy to implement.
//! - It's fast.
//!
//! ## Disadvantages
//!
//! - It's not very flexible. The connection ID is only valid for a certain
//! amount of time.
//! - It's not very accurate. The connection ID is valid for more than two
//! minutes.
use std::net::SocketAddr;
use std::panic::Location;

Expand All @@ -12,16 +80,19 @@ pub type SinceUnixEpochTimeExtent = TimeExtent;

pub const COOKIE_LIFETIME: TimeExtent = TimeExtent::from_sec(2, &60);

/// Converts a connection ID into a connection cookie.
#[must_use]
pub fn from_connection_id(connection_id: &ConnectionId) -> Cookie {
connection_id.0.to_le_bytes()
}

/// Converts a connection cookie into a connection ID.
#[must_use]
pub fn into_connection_id(connection_cookie: &Cookie) -> ConnectionId {
ConnectionId(i64::from_le_bytes(*connection_cookie))
}

/// Generates a new connection cookie.
#[must_use]
pub fn make(remote_address: &SocketAddr) -> Cookie {
let time_extent = cookie_builder::get_last_time_extent();
Expand All @@ -30,6 +101,8 @@ pub fn make(remote_address: &SocketAddr) -> Cookie {
cookie_builder::build(remote_address, &time_extent)
}

/// Checks if the supplied `connection_cookie` is valid.
///
/// # Panics
///
/// It would panic if the `COOKIE_LIFETIME` constant would be an unreasonably large number.
Expand Down
6 changes: 6 additions & 0 deletions src/servers/udp/error.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
//! Error types for the UDP server.
use std::panic::Location;

use thiserror::Error;
use torrust_tracker_located_error::LocatedError;

/// Error returned by the UDP server.
#[derive(Error, Debug)]
pub enum Error {
/// 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 from a third-party library (aquatic_udp_protocol).
#[error("internal server error: {message}, {location}")]
InternalServer {
location: &'static Location<'static>,
message: String,
},

/// Error returned when the connection id could not be verified.
#[error("connection id could not be verified")]
InvalidConnectionId { location: &'static Location<'static> },

/// Error returned when the request is invalid.
#[error("bad request: {source}")]
BadRequest {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
Expand Down
Loading