Skip to content

Commit

Permalink
Merge #273: Crate docs for servers::upd module
Browse files Browse the repository at this point in the history
7014a46 docs: [#271] crate docs for servers::udp mod (Jose Celano)

Pull request description:

  Documentation for the `crate::servers::udp` module. The UDP tracker.

ACKs for top commit:
  josecelano:
    ACK 7014a46

Tree-SHA512: 6c39d30c4163e3fc64a102aefb2e51715ceb7fe967124cea39ee6b678244c0bbe944a94901f59fed8e90bc03b44bdfae38cf3a29b9cbe4dd339ac295844078d4
  • Loading branch information
josecelano committed Apr 10, 2023
2 parents 6126679 + 7014a46 commit 046ca86
Show file tree
Hide file tree
Showing 15 changed files with 886 additions and 25 deletions.
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

0 comments on commit 046ca86

Please sign in to comment.