Skip to content

Commit 851ad77

Browse files
authored
Merge pull request #25 from torrust/development
update: v2.2.0
2 parents e9e992f + 9988c64 commit 851ad77

File tree

19 files changed

+690
-673
lines changed

19 files changed

+690
-673
lines changed

Cargo.lock

Lines changed: 25 additions & 435 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "torrust-tracker"
3-
version = "2.1.2"
3+
version = "2.2.0"
44
license = "AGPL-3.0"
55
authors = ["Mick van Dijke <[email protected]>"]
66
description = "A feature rich BitTorrent tracker."
@@ -24,13 +24,11 @@ log = {version = "0.4", features = ["release_max_level_info"]}
2424
fern = "0.6"
2525
chrono = "0.4"
2626
byteorder = "1"
27-
external-ip = "4.1.0"
2827
r2d2_sqlite = "0.16.0"
2928
r2d2 = "0.8.8"
3029
rand = "0.8.4"
31-
env_logger = "0.9.0"
3230
config = "0.11"
3331
derive_more = "0.99"
3432
thiserror = "1.0"
35-
36-
aquatic_udp_protocol = "0.1.0"
33+
aquatic_udp_protocol = { git = "https://github.com/greatest-ape/aquatic" }
34+
futures = "0.3.21"

README.md

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ Torrust Tracker is a lightweight but incredibly powerful and feature-rich BitTor
66

77

88
### Features
9-
* [X] UDP server
10-
* [X] HTTP (optional SSL) server
9+
* [X] Multiple UDP server and HTTP(S) server blocks for socket binding possible
10+
* [X] Full IPv4 and IPv6 support for both UDP and HTTP(S)
1111
* [X] Private & Whitelisted mode
1212
* [X] Built-in API
1313
* [X] Torrent whitelisting
1414
* [X] Peer authentication using time-bound keys
15+
* [X] newTrackon check supported for both HTTP, UDP, where IPv4 and IPv6 is properly handled
16+
* [X] SQLite3 Persistent loading and saving of the torrent hashes and completed count
1517

1618
### Implemented BEPs
1719
* [BEP 3](https://www.bittorrent.org/beps/bep_0003.html): The BitTorrent Protocol
@@ -46,22 +48,31 @@ cargo build --release
4648

4749
* Edit the newly created config.toml file according to your liking, see [configuration documentation](https://torrust.github.io/torrust-documentation/torrust-tracker/config/). Eg:
4850
```toml
49-
log_level = "trace"
51+
log_level = "info"
5052
mode = "public"
5153
db_path = "data.db"
54+
persistence = false
5255
cleanup_interval = 600
53-
external_ip = "YOUR_EXTERNAL_IP"
56+
cleanup_peerless = true
57+
external_ip = "0.0.0.0"
58+
announce_interval = 120
59+
announce_interval_min = 900
60+
peer_timeout = 900
61+
on_reverse_proxy = false
5462

55-
[udp_tracker]
63+
[[udp_trackers]]
64+
enabled = false
5665
bind_address = "0.0.0.0:6969"
57-
announce_interval = 120
5866

59-
[http_tracker]
67+
[[udp_trackers]]
68+
enabled = true
69+
bind_address = "[::]:6969"
70+
71+
[[http_trackers]]
6072
enabled = true
6173
bind_address = "0.0.0.0:6969"
62-
on_reverse_proxy = false
63-
announce_interval = 120
6474
ssl_enabled = false
75+
ssl_bind_address = "0.0.0.0:6868"
6576
ssl_cert_path = ""
6677
ssl_key_path = ""
6778

@@ -80,7 +91,7 @@ admin = "MyAccessToken"
8091
```
8192

8293
### Tracker URL
83-
Your tracker announce URL will be **udp://{tracker-ip:port}** or **https://{tracker-ip:port}/announce** depending on your tracker mode.
94+
Your tracker announce URL will be **udp://{tracker-ip:port}** and/or **http://{tracker-ip:port}/announce** and/or **https://{tracker-ip:port}/announce** depending on your bindings.
8495
In private & private_listed mode, tracker keys are added after the tracker URL like: **https://{tracker-ip:port}/announce/{key}**.
8596

8697
### Built-in API
@@ -89,3 +100,4 @@ Read the API documentation [here](https://torrust.github.io/torrust-documentatio
89100
### Credits
90101
This project was a joint effort by [Nautilus Cyberneering GmbH](https://nautilus-cyberneering.de/) and [Dutch Bits](https://dutchbits.nl).
91102
Also thanks to [Naim A.](https://github.com/naim94a/udpt) and [greatest-ape](https://github.com/greatest-ape/aquatic) for some parts of the code.
103+
Further added features and functions thanks to [Power2All](https://github.com/power2all).

src/common.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ impl serde::ser::Serialize for InfoHash {
8484
let mut buffer = [0u8; 40];
8585
let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok().unwrap();
8686
let str_out = std::str::from_utf8(bytes_out).unwrap();
87-
8887
serializer.serialize_str(str_out)
8988
}
9089
}
@@ -131,8 +130,12 @@ pub struct PeerId(pub [u8; 20]);
131130
impl PeerId {
132131
pub fn to_string(&self) -> String {
133132
let mut buffer = [0u8; 20];
134-
let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok().unwrap();
135-
String::from(std::str::from_utf8(bytes_out).unwrap())
133+
let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok();
134+
return if let Some(bytes_out) = bytes_out {
135+
String::from(std::str::from_utf8(bytes_out).unwrap())
136+
} else {
137+
"".to_string()
138+
}
136139
}
137140
}
138141

src/config.rs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,16 @@ pub enum TrackerServer {
1717

1818
#[derive(Serialize, Deserialize)]
1919
pub struct UdpTrackerConfig {
20+
pub enabled: bool,
2021
pub bind_address: String,
21-
pub announce_interval: u32,
2222
}
2323

2424
#[derive(Serialize, Deserialize)]
2525
pub struct HttpTrackerConfig {
2626
pub enabled: bool,
2727
pub bind_address: String,
28-
pub on_reverse_proxy: bool,
29-
pub announce_interval: u32,
3028
pub ssl_enabled: bool,
29+
pub ssl_bind_address: String,
3130
#[serde(serialize_with = "none_as_empty_string")]
3231
pub ssl_cert_path: Option<String>,
3332
#[serde(serialize_with = "none_as_empty_string")]
@@ -52,10 +51,16 @@ pub struct Configuration {
5251
pub log_level: Option<String>,
5352
pub mode: TrackerMode,
5453
pub db_path: String,
54+
pub persistence: bool,
5555
pub cleanup_interval: Option<u64>,
56+
pub cleanup_peerless: bool,
5657
pub external_ip: Option<String>,
57-
pub udp_tracker: UdpTrackerConfig,
58-
pub http_tracker: HttpTrackerConfig,
58+
pub announce_interval: u32,
59+
pub announce_interval_min: u32,
60+
pub peer_timeout: u32,
61+
pub on_reverse_proxy: bool,
62+
pub udp_trackers: Vec<UdpTrackerConfig>,
63+
pub http_trackers: Vec<HttpTrackerConfig>,
5964
pub http_api: HttpApiConfig,
6065
}
6166

@@ -124,36 +129,48 @@ impl Configuration {
124129

125130
impl Configuration {
126131
pub fn default() -> Configuration {
127-
Configuration {
132+
let mut configuration = Configuration {
128133
log_level: Option::from(String::from("info")),
129134
mode: TrackerMode::PublicMode,
130135
db_path: String::from("data.db"),
136+
persistence: false,
131137
cleanup_interval: Some(600),
138+
cleanup_peerless: true,
132139
external_ip: Some(String::from("0.0.0.0")),
133-
udp_tracker: UdpTrackerConfig {
134-
bind_address: String::from("0.0.0.0:6969"),
135-
announce_interval: 120,
136-
},
137-
http_tracker: HttpTrackerConfig {
140+
announce_interval: 120,
141+
announce_interval_min: 120,
142+
peer_timeout: 900,
143+
on_reverse_proxy: false,
144+
udp_trackers: Vec::new(),
145+
http_trackers: Vec::new(),
146+
http_api: HttpApiConfig {
147+
enabled: true,
148+
bind_address: String::from("127.0.0.1:1212"),
149+
access_tokens: [(String::from("admin"), String::from("MyAccessToken"))].iter().cloned().collect(),
150+
}
151+
};
152+
configuration.udp_trackers.push(
153+
UdpTrackerConfig{
154+
enabled: false,
155+
bind_address: String::from("0.0.0.0:6969")
156+
}
157+
);
158+
configuration.http_trackers.push(
159+
HttpTrackerConfig{
138160
enabled: false,
139161
bind_address: String::from("0.0.0.0:6969"),
140-
on_reverse_proxy: false,
141-
announce_interval: 120,
142162
ssl_enabled: false,
163+
ssl_bind_address: String::from("0.0.0.0:6868"),
143164
ssl_cert_path: None,
144165
ssl_key_path: None
145-
},
146-
http_api: HttpApiConfig {
147-
enabled: true,
148-
bind_address: String::from("127.0.0.1:1212"),
149-
access_tokens: [(String::from("admin"), String::from("MyAccessToken"))].iter().cloned().collect(),
150-
},
151-
}
166+
}
167+
);
168+
configuration
152169
}
153170

154171
pub fn verify(&self) -> Result<(), ConfigurationError> {
155172
// UDP is not secure for sending private keys
156-
if (self.mode == TrackerMode::PrivateMode || self.mode == TrackerMode::PrivateListedMode) && self.get_tracker_server() == TrackerServer::UDP {
173+
if self.mode == TrackerMode::PrivateMode || self.mode == TrackerMode::PrivateListedMode {
157174
return Err(ConfigurationError::TrackerModeIncompatible)
158175
}
159176

@@ -188,12 +205,4 @@ impl Configuration {
188205
fs::write("config.toml", toml_string).expect("Could not write to file!");
189206
Ok(())
190207
}
191-
192-
pub fn get_tracker_server(&self) -> TrackerServer {
193-
if self.http_tracker.enabled {
194-
TrackerServer::HTTP
195-
} else {
196-
TrackerServer::UDP
197-
}
198-
}
199208
}

src/database.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::{InfoHash, AUTH_KEY_LENGTH};
1+
use std::collections::BTreeMap;
2+
use crate::{InfoHash, AUTH_KEY_LENGTH, TorrentEntry};
23
use log::debug;
34
use r2d2_sqlite::{SqliteConnectionManager, rusqlite};
45
use r2d2::{Pool};
@@ -32,6 +33,13 @@ impl SqliteDatabase {
3233
info_hash VARCHAR(20) NOT NULL UNIQUE
3334
);".to_string();
3435

36+
let create_torrents_table = "
37+
CREATE TABLE IF NOT EXISTS torrents (
38+
id integer PRIMARY KEY AUTOINCREMENT,
39+
info_hash VARCHAR(20) NOT NULL UNIQUE,
40+
completed INTEGER DEFAULT 0 NOT NULL
41+
);".to_string();
42+
3543
let create_keys_table = format!("
3644
CREATE TABLE IF NOT EXISTS keys (
3745
id integer PRIMARY KEY AUTOINCREMENT,
@@ -43,7 +51,15 @@ impl SqliteDatabase {
4351
match conn.execute(&create_whitelist_table, NO_PARAMS) {
4452
Ok(updated) => {
4553
match conn.execute(&create_keys_table, NO_PARAMS) {
46-
Ok(updated2) => Ok(updated + updated2),
54+
Ok(updated2) => {
55+
match conn.execute(&create_torrents_table, NO_PARAMS) {
56+
Ok(updated3) => Ok(updated + updated2 + updated3),
57+
Err(e) => {
58+
debug!("{:?}", e);
59+
Err(e)
60+
}
61+
}
62+
}
4763
Err(e) => {
4864
debug!("{:?}", e);
4965
Err(e)
@@ -57,6 +73,36 @@ impl SqliteDatabase {
5773
}
5874
}
5975

76+
pub async fn load_persistent_torrent_data(&self) -> Result<Vec<(InfoHash, u32)>, rusqlite::Error> {
77+
let conn = self.pool.get().unwrap();
78+
let mut stmt = conn.prepare("SELECT info_hash, completed FROM torrents")?;
79+
80+
let torrent_iter = stmt.query_map(NO_PARAMS, |row| {
81+
let info_hash_string: String = row.get(0)?;
82+
let info_hash = InfoHash::from_str(&info_hash_string).unwrap();
83+
let completed: u32 = row.get(1)?;
84+
Ok((info_hash, completed))
85+
})?;
86+
87+
let torrents: Vec<(InfoHash, u32)> = torrent_iter.filter_map(|x| x.ok() ).collect();
88+
89+
Ok(torrents)
90+
}
91+
92+
pub async fn save_persistent_torrent_data(&self, torrents: &BTreeMap<InfoHash, TorrentEntry>) -> Result<(), rusqlite::Error> {
93+
let mut conn = self.pool.get().unwrap();
94+
let db_transaction = conn.transaction()?;
95+
96+
for (info_hash, torrent_entry) in torrents {
97+
let (_seeders, completed, _leechers) = torrent_entry.get_stats();
98+
let _ = db_transaction.execute("INSERT OR REPLACE INTO torrents (info_hash, completed) VALUES (?, ?)", &[info_hash.to_string(), completed.to_string()]);
99+
}
100+
101+
let _ = db_transaction.commit();
102+
103+
Ok(())
104+
}
105+
60106
pub async fn get_info_hash_from_whitelist(&self, info_hash: &str) -> Result<InfoHash, rusqlite::Error> {
61107
let conn = self.pool.get().unwrap();
62108
let mut stmt = conn.prepare("SELECT info_hash FROM whitelist WHERE info_hash = ?")?;

0 commit comments

Comments
 (0)