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
474 changes: 15 additions & 459 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "torrust-tracker"
version = "2.1.2"
version = "2.2.0"
license = "AGPL-3.0"
authors = ["Mick van Dijke <mick@dutchbits.nl>"]
description = "A feature rich BitTorrent tracker."
Expand All @@ -24,11 +24,9 @@ log = {version = "0.4", features = ["release_max_level_info"]}
fern = "0.6"
chrono = "0.4"
byteorder = "1"
external-ip = "4.1.0"
r2d2_sqlite = "0.16.0"
r2d2 = "0.8.8"
rand = "0.8.4"
env_logger = "0.9.0"
config = "0.11"
derive_more = "0.99"
thiserror = "1.0"
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ Torrust Tracker is a lightweight but incredibly powerful and feature-rich BitTor


### Features
* [X] UDP server
* [X] HTTP and/or HTTPS (SSL) server
* [X] Multiple UDP and HTTP(S) blocks for socket binding possible
* [X] Multiple UDP server and HTTP(S) server blocks for socket binding possible
* [X] Full IPv4 and IPv6 support for both UDP and HTTP(S)
* [X] Private & Whitelisted mode
* [X] Built-in API
* [X] Torrent whitelisting
* [X] Peer authentication using time-bound keys
* [ ] NewTrackOn check supported
* [X] newTrackon check supported for both HTTP, UDP, where IPv4 and IPv6 is properly handled
* [X] SQLite3 Persistent loading and saving of the torrent hashes and completed count

### Implemented BEPs
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub struct Configuration {
pub cleanup_peerless: bool,
pub external_ip: Option<String>,
pub announce_interval: u32,
pub announce_interval_min: u32,
pub peer_timeout: u32,
pub on_reverse_proxy: bool,
pub udp_trackers: Vec<UdpTrackerConfig>,
Expand Down Expand Up @@ -137,6 +138,7 @@ impl Configuration {
cleanup_peerless: true,
external_ip: Some(String::from("0.0.0.0")),
announce_interval: 120,
announce_interval_min: 120,
peer_timeout: 900,
on_reverse_proxy: false,
udp_trackers: Vec::new(),
Expand Down
12 changes: 8 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::net::SocketAddr;
use log::{info};
use torrust_tracker::{http_api_server, Configuration, TorrentTracker, UdpServer, HttpTrackerConfig, UdpTrackerConfig, HttpApiConfig, logging};
use std::sync::Arc;
use log::info;
use tokio::task::JoinHandle;
use torrust_tracker::{Configuration, http_api_server, HttpApiConfig, HttpTrackerConfig, logging, TorrentTracker, UdpServer, UdpTrackerConfig};
use torrust_tracker::torrust_http_tracker::server::HttpServer;

#[tokio::main]
Expand Down Expand Up @@ -83,7 +83,7 @@ fn start_torrent_cleanup_job(config: Arc<Configuration>, tracker: Arc<TorrentTra
break;
}
}
}))
}));
}

fn start_api_server(config: &HttpApiConfig, tracker: Arc<TorrentTracker>) -> JoinHandle<()> {
Expand All @@ -92,7 +92,11 @@ fn start_api_server(config: &HttpApiConfig, tracker: Arc<TorrentTracker>) -> Joi

tokio::spawn(async move {
let server = http_api_server::build_server(tracker);
server.bind(bind_addr).await;
let _ = server.bind_with_graceful_shutdown(bind_addr, async move {
tokio::signal::ctrl_c()
.await
.expect("failed to listen to shutdown signal");
});
})
}

Expand Down
5 changes: 3 additions & 2 deletions src/torrust_http_tracker/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub async fn handle_announce(announce_request: AnnounceRequest, auth_key: Option
}
});
let announce_interval = tracker.config.announce_interval;
send_announce_response(&announce_request, torrent_stats, peers, announce_interval)
send_announce_response(&announce_request, torrent_stats, peers, announce_interval, tracker.config.announce_interval_min)
}

/// Handle scrape request
Expand Down Expand Up @@ -127,7 +127,7 @@ pub async fn handle_error(r: Rejection) -> std::result::Result<impl Reply, Infal
}

/// Send announce response
fn send_announce_response(announce_request: &AnnounceRequest, torrent_stats: TorrentStats, peers: Vec<TorrentPeer>, interval: u32) -> WebResult<impl Reply> {
fn send_announce_response(announce_request: &AnnounceRequest, torrent_stats: TorrentStats, peers: Vec<TorrentPeer>, interval: u32, interval_min: u32) -> WebResult<impl Reply> {
let http_peers: Vec<Peer> = peers.iter().map(|peer| Peer {
peer_id: peer.peer_id.to_string(),
ip: peer.peer_addr.ip(),
Expand All @@ -136,6 +136,7 @@ fn send_announce_response(announce_request: &AnnounceRequest, torrent_stats: Tor

let res = AnnounceResponse {
interval,
interval_min,
complete: torrent_stats.seeders,
incomplete: torrent_stats.leechers,
peers: http_peers
Expand Down
3 changes: 3 additions & 0 deletions src/torrust_http_tracker/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct Peer {
#[derive(Serialize)]
pub struct AnnounceResponse {
pub interval: u32,
pub interval_min: u32,
//pub tracker_id: String,
pub complete: u32,
pub incomplete: u32,
Expand Down Expand Up @@ -45,6 +46,8 @@ impl AnnounceResponse {
let mut bytes: Vec<u8> = Vec::new();
bytes.write(b"d8:intervali")?;
bytes.write(&self.interval.to_string().as_bytes())?;
bytes.write(b"d12:min intervali")?;
bytes.write(&self.interval.to_string().as_bytes())?;
bytes.write(b"e8:completei")?;
bytes.write(&self.complete.to_string().as_bytes())?;
bytes.write(b"e10:incompletei")?;
Expand Down
18 changes: 14 additions & 4 deletions src/torrust_http_tracker/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@ impl HttpServer {

/// Start the HttpServer
pub async fn start(&self, socket_addr: SocketAddr) {
warp::serve(routes(self.tracker.clone()))
.run(socket_addr).await;
let (_addr, server) = warp::serve(routes(self.tracker.clone()))
.bind_with_graceful_shutdown(socket_addr, async move {
tokio::signal::ctrl_c()
.await
.expect("failed to listen to shutdown signal");
});
tokio::task::spawn(server);
}

/// Start the HttpServer in TLS mode
pub async fn start_tls(&self, socket_addr: SocketAddr, ssl_cert_path: &str, ssl_key_path: &str) {
warp::serve(routes(self.tracker.clone()))
let (_addr, server) = warp::serve(routes(self.tracker.clone()))
.tls()
.cert_path(ssl_cert_path)
.key_path(ssl_key_path)
.run(socket_addr).await;
.bind_with_graceful_shutdown(socket_addr, async move {
tokio::signal::ctrl_c()
.await
.expect("failed to listen to shutdown signal");
});
tokio::task::spawn(server);
}
}
27 changes: 27 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::net::SocketAddr;
use std::time::SystemTime;
use std::error::Error;
use std::fmt::Write;
use std::io::Cursor;
use aquatic_udp_protocol::ConnectionId;
use byteorder::{BigEndian, ReadBytesExt};

pub fn get_connection_id(remote_address: &SocketAddr) -> ConnectionId {
match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
Expand All @@ -29,3 +31,28 @@ pub fn url_encode_bytes(content: &[u8]) -> Result<String, Box<dyn Error>> {

Ok(out)
}

// Function that will convert a small or big number into the smallest form of a byte array.
pub async fn convert_int_to_bytes(number: &u64) -> Vec<u8> {
let mut return_data: Vec<u8> = Vec::new();
// return_data.extend(number.to_be_bytes().reverse());
for i in 1..8 {
if number < &256u64.pow(i) {
let start: usize = 16usize - i as usize;
return_data.extend(number.to_be_bytes()[start..8].iter());
return return_data;
}
}
return return_data;
}

pub async fn convert_bytes_to_int(array: &Vec<u8>) -> u64 {
let mut array_fixed: Vec<u8> = Vec::new();
let size = 8 - array.len();
for _ in 0..size {
array_fixed.push(0);
}
array_fixed.extend(array);
let mut rdr = Cursor::new(array_fixed);
return rdr.read_u64::<BigEndian>().unwrap();
}