Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f6cd282
wip: support for multiple db drivers
mickvandijke Apr 5, 2022
3763b4b
Merge branch 'torrust:development' into development
Power2All Apr 10, 2022
a995638
Fixing torrent cleanup, adding defragmentation of memory, and fixed r…
Power2All Apr 10, 2022
f7a6ca7
Merge pull request #27 from Power2All/development
mickvandijke Apr 10, 2022
5baea30
feat: added MySQL database support
mickvandijke Apr 10, 2022
f407b9e
fix: error when running in PrivateMode or PrivateListedMode
mickvandijke Apr 11, 2022
12523c4
Bypassing a error
Power2All Apr 12, 2022
d994ccf
Merge pull request #30 from Power2All/development
mickvandijke Apr 12, 2022
64fd271
fix: fixed PeerKeyInvalid error when using MySQL
mickvandijke Apr 12, 2022
fa330c7
fix: stats tracker now runs on a dedicated thread instead of spawning…
mickvandijke Apr 13, 2022
6cec6f2
Adding periodic
Power2All Apr 13, 2022
f7b6c4e
Fix the saving...
Power2All Apr 13, 2022
f581289
Showing information that it's saving periodically
Power2All Apr 13, 2022
2045b39
Adding memory logging
Power2All Apr 13, 2022
41785b6
Typo ...
Power2All Apr 13, 2022
fba57c3
Merge pull request #31 from Power2All/development
mickvandijke Apr 13, 2022
c6dff90
Adding a statistics viewing in console
Power2All Apr 13, 2022
97c17d4
Oops :)
Power2All Apr 13, 2022
5242b97
Merge pull request #32 from Power2All/development
mickvandijke Apr 13, 2022
145780a
Adding profiling and updates and cleanups
Power2All Apr 15, 2022
ff4ca58
Merge branch 'development' of https://github.com/Power2All/torrust-tr…
Power2All Apr 15, 2022
9159187
First implementation and iteration of using crossbeam to eventually h…
Power2All Apr 15, 2022
03eade1
Moving around, and preparing a refactoring to channel based handling
Power2All Apr 15, 2022
0a858dc
Revert "Moving around, and preparing a refactoring to channel based h…
Power2All Apr 16, 2022
bd680c4
Revert "First implementation and iteration of using crossbeam to even…
Power2All Apr 16, 2022
509d487
Code cleanup and fixing MySQL
Power2All Apr 16, 2022
0604cb9
FIxing MySQL support
Power2All Apr 16, 2022
3a839cd
Fixing SQL issue and locking problem
Power2All Apr 17, 2022
93b0537
feat: added statistics config option
mickvandijke Apr 17, 2022
1f75e58
Merge branch 'development' into development
mickvandijke Apr 17, 2022
28ce47b
Merge pull request #33 from Power2All/development
mickvandijke Apr 17, 2022
9d0714d
Updating README
Power2All Apr 17, 2022
8646a54
Merge branch 'development' of https://github.com/Power2All/torrust-tr…
Power2All Apr 17, 2022
9bc1ebb
Fixing possible bug
Power2All Apr 17, 2022
0c332cf
Fixing a buggy SQLite3 and MySQL
Power2All Apr 17, 2022
84c6ca4
Speeding up inserting
Power2All Apr 17, 2022
8ebd1e5
Typo's
Power2All Apr 17, 2022
1b4deca
Looking for reason of timeout
Power2All Apr 17, 2022
035c28d
Improving locking
Power2All Apr 17, 2022
aea2de0
Typo
Power2All Apr 17, 2022
f331c08
Merge pull request #34 from Power2All/development
mickvandijke Apr 25, 2022
5d1ad9d
Revert "Development"
mickvandijke Apr 25, 2022
4cf4e95
Merge pull request #38 from torrust/revert-34-development
mickvandijke Apr 25, 2022
cef2016
Revert "Revert "Development""
mickvandijke Apr 25, 2022
2d4ddcf
Merge pull request #39 from torrust/revert-38-revert-34-development
mickvandijke Apr 25, 2022
6578c35
chore: removed dhat
mickvandijke May 2, 2022
52785a0
refactor: renamed http and udp modules
mickvandijke May 2, 2022
5a6eaa4
refactor: moved databases to own module
mickvandijke May 5, 2022
4bf0c8b
refactor: major refactor of all code
mickvandijke May 6, 2022
047c2db
refactor: refactored even more code
mickvandijke May 6, 2022
6fcc043
refactor: removed unused enum in config
mickvandijke May 6, 2022
abe2e68
refactor: removed unused byteorder dependency
mickvandijke May 7, 2022
d6dd240
refactor: updated rust version to 2021
mickvandijke May 7, 2022
278ac3a
refactor: updated cargo.toml dev and release profiles
mickvandijke May 7, 2022
829afb9
refactor: refactored and improved tracker.cleanup_torrents()
mickvandijke May 7, 2022
eb69fa0
feat: added strip to Cargo.toml to almost half compiled binary size o…
mickvandijke May 7, 2022
1d353ca
feat: massively improved key authentication speed in private mode
mickvandijke May 7, 2022
aac9ac7
fix: http scrape response
mickvandijke May 8, 2022
c4c6c3d
feat: improved persistent torrent saving
mickvandijke May 9, 2022
0a77131
fix: fixed up some database errors
mickvandijke May 9, 2022
68d3242
some code cleanup
mickvandijke May 9, 2022
ce24446
feat: scrape unauthenticated info_hashes return 0 values instead of e…
mickvandijke May 9, 2022
77e173c
refactor: removed root from http routes
mickvandijke May 9, 2022
b9aca67
feat: added endpoints to reload whitelist and keys from database
mickvandijke May 9, 2022
df87a9d
updated example config
mickvandijke May 9, 2022
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
1,612 changes: 1,267 additions & 345 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 27 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
[package]
edition = "2021"
name = "torrust-tracker"
version = "2.2.0"
version = "2.3.0"
license = "AGPL-3.0"
authors = ["Mick van Dijke <mick@dutchbits.nl>"]
description = "A feature rich BitTorrent tracker."
edition = "2018"
repository = "https://github.com/torrust/torrust-tracker"

[profile.dev]
debug = 1
opt-level = 1
lto = "thin"

[profile.release]
debug = 1
opt-level = 3
lto = "fat"
strip = true

[dependencies]
serde = {version = "1.0", features = ["derive"]}
tokio = { version = "1.7", features = ["full"] }

serde = { version = "1.0", features = ["derive"] }
serde_bencode = "^0.2.3"
serde_bytes = "0.11"
serde_json = "1.0.72"
hex = "0.4.3"
percent-encoding = "2.1.0"
warp = {version = "0.3", features = ["tls"]}
tokio = {version = "1.7", features = ["macros", "io-util", "net", "time", "rt-multi-thread", "fs", "sync", "signal"]}
binascii = "0.1"

warp = { version = "0.3", features = ["tls"] }

config = "0.11"
toml = "0.5"
log = {version = "0.4", features = ["release_max_level_info"]}

log = { version = "0.4", features = ["release_max_level_info"] }
fern = "0.6"
chrono = "0.4"
byteorder = "1"
r2d2_sqlite = "0.16.0"

r2d2 = "0.8.8"
r2d2_mysql = "21.0.0"
r2d2_sqlite = "0.16.0"

rand = "0.8.4"
config = "0.11"
derive_more = "0.99"
thiserror = "1.0"
aquatic_udp_protocol = { git = "https://github.com/greatest-ape/aquatic" }
futures = "0.3.21"
async-trait = "0.1.52"

aquatic_udp_protocol = "0.2.0"
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Torrust Tracker is a lightweight but incredibly powerful and feature-rich BitTor
* [X] Peer authentication using time-bound keys
* [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
* [X] MySQL support added as engine option
* [X] Periodically saving added, interval can be configured

### Implemented BEPs
* [BEP 3](https://www.bittorrent.org/beps/bep_0003.html): The BitTorrent Protocol
Expand Down Expand Up @@ -50,29 +52,26 @@ cargo build --release
```toml
log_level = "info"
mode = "public"
db_driver = "Sqlite3"
db_path = "data.db"
persistence = false
cleanup_interval = 600
cleanup_peerless = true
external_ip = "0.0.0.0"
announce_interval = 120
announce_interval_min = 900
peer_timeout = 900
min_announce_interval = 120
max_peer_timeout = 900
on_reverse_proxy = false
external_ip = "0.0.0.0"
tracker_usage_statistics = true
persistent_torrent_completed_stat = false
inactive_peer_cleanup_interval = 600
remove_peerless_torrents = true

[[udp_trackers]]
enabled = false
bind_address = "0.0.0.0:6969"

[[udp_trackers]]
enabled = true
bind_address = "[::]:6969"

[[http_trackers]]
enabled = true
bind_address = "0.0.0.0:6969"
ssl_enabled = false
ssl_bind_address = "0.0.0.0:6868"
ssl_cert_path = ""
ssl_key_path = ""

Expand Down
1 change: 1 addition & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod server;
91 changes: 73 additions & 18 deletions src/http_api_server.rs → src/api/server.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::tracker::{TorrentTracker};
use serde::{Deserialize, Serialize};
use std::cmp::min;
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use std::sync::Arc;
use warp::{filters, reply, reply::Reply, serve, Filter, Server};
use crate::TorrentPeer;
use super::common::*;

use serde::{Deserialize, Serialize};
use warp::{Filter, filters, reply, serve};

use crate::protocol::common::*;
use crate::peer::TorrentPeer;
use crate::tracker::tracker::TorrentTracker;

#[derive(Deserialize, Debug)]
struct TorrentInfoQuery {
Expand All @@ -20,7 +23,7 @@ struct Torrent<'a> {
completed: u32,
leechers: u32,
#[serde(skip_serializing_if = "Option::is_none")]
peers: Option<Vec<TorrentPeer>>,
peers: Option<Vec<&'a TorrentPeer>>,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -52,7 +55,7 @@ enum ActionStatus<'a> {

impl warp::reject::Reject for ActionStatus<'static> {}

fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Error = warp::reject::Rejection> + Clone {
fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract=(), Error=warp::reject::Rejection> + Clone {
#[derive(Deserialize)]
struct AuthToken {
token: Option<String>,
Expand All @@ -69,7 +72,7 @@ fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Er
match token.token {
Some(token) => {
if !tokens.contains(&token) {
return Err(warp::reject::custom(ActionStatus::Err { reason: "token not valid".into() }))
return Err(warp::reject::custom(ActionStatus::Err { reason: "token not valid".into() }));
}

Ok(())
Expand All @@ -81,7 +84,7 @@ fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Er
.untuple_one()
}

pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract = impl Reply> + Clone + Send + Sync + 'static> {
pub fn start(socket_addr: SocketAddr, tracker: Arc<TorrentTracker>) -> impl warp::Future<Output = ()> {
// GET /api/torrents?offset=:u32&limit=:u32
// View torrent list
let api_torrents = tracker.clone();
Expand Down Expand Up @@ -131,7 +134,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
let mut results = Stats{
let mut results = Stats {
torrents: 0,
seeders: 0,
completed: 0,
Expand All @@ -147,9 +150,11 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
udp4_scrapes_handled: 0,
udp6_connections_handled: 0,
udp6_announces_handled: 0,
udp6_scrapes_handled: 0
udp6_scrapes_handled: 0,
};

let db = tracker.get_torrents().await;

let _: Vec<_> = db
.iter()
.map(|(_info_hash, torrent_entry)| {
Expand All @@ -160,7 +165,9 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
results.torrents += 1;
})
.collect();

let stats = tracker.get_stats().await;

results.tcp4_connections_handled = stats.tcp4_connections_handled as u32;
results.tcp4_announces_handled = stats.tcp4_announces_handled as u32;
results.tcp4_scrapes_handled = stats.tcp4_scrapes_handled as u32;
Expand Down Expand Up @@ -195,7 +202,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
let torrent_entry_option = db.get(&info_hash);

if torrent_entry_option.is_none() {
return Err(warp::reject::custom(ActionStatus::Err { reason: "torrent does not exist".into() }))
return Result::<_, warp::reject::Rejection>::Ok(reply::json(&"torrent not known"))
}

let torrent_entry = torrent_entry_option.unwrap();
Expand Down Expand Up @@ -226,10 +233,10 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|(info_hash, tracker): (InfoHash, Arc<TorrentTracker>)| {
async move {
match tracker.remove_torrent_from_whitelist(&info_hash).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
}
match tracker.remove_torrent_from_whitelist(&info_hash).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
}
}
});

Expand Down Expand Up @@ -286,13 +293,53 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|(key, tracker): (String, Arc<TorrentTracker>)| {
async move {
match tracker.remove_auth_key(key).await {
match tracker.remove_auth_key(&key).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to delete key".into() }))
}
}
});

// GET /api/whitelist/reload
// Reload whitelist
let t7 = tracker.clone();
let reload_whitelist = filters::method::get()
.and(filters::path::path("whitelist"))
.and(filters::path::path("reload"))
.and(filters::path::end())
.map(move || {
let tracker = t7.clone();
tracker
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
match tracker.load_whitelist().await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload whitelist".into() }))
}
}
});

// GET /api/keys/reload
// Reload whitelist
let t8 = tracker.clone();
let reload_keys = filters::method::get()
.and(filters::path::path("keys"))
.and(filters::path::path("reload"))
.and(filters::path::end())
.map(move || {
let tracker = t8.clone();
tracker
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
match tracker.load_keys().await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload keys".into() }))
}
}
});

let api_routes =
filters::path::path("api")
.and(view_torrent_list
Expand All @@ -302,9 +349,17 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
.or(add_torrent)
.or(create_key)
.or(delete_key)
.or(reload_whitelist)
.or(reload_keys)
);

let server = api_routes.and(authenticate(tracker.config.http_api.access_tokens.clone()));

serve(server)
let (_addr, api_server) = serve(server).bind_with_graceful_shutdown(socket_addr, async move {
tokio::signal::ctrl_c()
.await
.expect("Failed to listen to shutdown signal.");
});

api_server
}
Loading