Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
9ee34f8
test: add test for Instant serialization
josecelano Aug 10, 2022
27e04e6
docs: mod udp tracker description
josecelano Aug 10, 2022
d8b13e5
test: added temp test for udp tracker connetion id
josecelano Aug 10, 2022
6c2f312
test: fix UDP conn ID generation test by mocking current time
josecelano Aug 11, 2022
bfa803b
refactor: rename function
josecelano Aug 12, 2022
33bc38d
test: updated connection id tests to test for BEP-15 requirements
mickvandijke Aug 11, 2022
5c0551e
feat: improved get_connection_id function to conform with the require…
mickvandijke Aug 12, 2022
6e1689e
test: added benchmarks to test the speed of generating a connection id
mickvandijke Aug 12, 2022
2f16d13
refactor: renamed variables and removed unused import
mickvandijke Aug 12, 2022
2255f22
Merge pull request #67 from WarmBeer/protocol-tests
mickvandijke Aug 12, 2022
a10624c
test: add test for Instant serialization
josecelano Aug 10, 2022
925344a
docs: mod udp tracker description
josecelano Aug 10, 2022
52390bf
test: added temp test for udp tracker connetion id
josecelano Aug 10, 2022
38e65e6
test: fix UDP conn ID generation test by mocking current time
josecelano Aug 11, 2022
0fa29b2
refactor: rename function
josecelano Aug 12, 2022
8667652
test: updated connection id tests to test for BEP-15 requirements
mickvandijke Aug 11, 2022
fddaaac
feat: improved get_connection_id function to conform with the require…
mickvandijke Aug 12, 2022
f36e673
test: added benchmarks to test the speed of generating a connection id
mickvandijke Aug 12, 2022
3880891
refactor: renamed variables and removed unused import
mickvandijke Aug 12, 2022
0f2beba
test: fix test for new implementation of get_connection_id
josecelano Aug 16, 2022
de1e71a
Merge branch 'protocol-tests' of github.com:torrust/torrust-tracker i…
josecelano Aug 16, 2022
55bb73c
test: add test for UDB connection ID verification
josecelano Aug 16, 2022
0feea82
refactor: move get_connection_id to its module inside UDP module
josecelano Aug 16, 2022
89d4f70
refactor: remove duplciate function
josecelano Aug 16, 2022
692a92d
refactor: move Instant serializer to its own mod in tracker mod
josecelano Aug 16, 2022
bf639b8
refactor: rename variable
josecelano Aug 16, 2022
eb679a7
feat: change server secret for udp connection id
josecelano Aug 16, 2022
897697d
refactor: change type and inject server secret for hashing connection id
josecelano Aug 16, 2022
f2d7d5f
refactor: wip. extract logic to generate time bound pepper
josecelano Aug 16, 2022
4769a49
refactor: extract struct ByteArray32
josecelano Aug 17, 2022
5b42af2
refactor: move time bound pepper to a new mod
josecelano Aug 17, 2022
a444bce
refactor: rename tests
josecelano Aug 17, 2022
a8322ed
refactor: extract struct TimeBoundPepper
josecelano Aug 17, 2022
e3b7b6e
refactor: create alias type Timestamp
josecelano Aug 17, 2022
55982fe
feat: implemented @da2ce7's proposal for TimeBoundPepper
josecelano Aug 17, 2022
1ba7c39
refactor: extract ByteArray32 constructors
josecelano Aug 17, 2022
3287448
docs: add comments
josecelano Aug 17, 2022
842487c
docs: fix typo
josecelano Aug 17, 2022
e6afa46
test: ByteArray32 converter from socker port (u16)
josecelano Aug 17, 2022
a2d1849
docs: add description for connection_id mod
josecelano Aug 18, 2022
c7637a8
docs: add properties for connection id
da2ce7 Aug 31, 2022
2b2ec2b
refactor: WIP. Encrypted connection id
josecelano Aug 31, 2022
13cf81e
refactor: add rust-crypto dependency for blowfish cypher
josecelano Sep 1, 2022
e6a205b
refactor: add encryption to connection id
josecelano Sep 1, 2022
a33db6d
refactor: extract functions
josecelano Sep 1, 2022
88ca2f4
refactor: rename varaiblbe
josecelano Sep 2, 2022
1b651d5
refactor: extract function
josecelano Sep 2, 2022
430c5dd
refactor:extract functions
josecelano Sep 2, 2022
e065f46
refactor: removed unused test
josecelano Sep 2, 2022
d6e428f
refactor: extract mod for Timestamp
josecelano Sep 2, 2022
2f92919
refactor: remove unused mod time_bound_pepper
josecelano Sep 2, 2022
c4115b1
refactor: create mod for connection id
josecelano Sep 2, 2022
5f613a6
refactor: store expiration time in connection id
josecelano Sep 2, 2022
ec0915d
docs: remove false test from test execution output
josecelano Sep 2, 2022
9511174
refactor: extract struct ClientId
josecelano Sep 2, 2022
f9a136d
test: add test for encryption
josecelano Sep 3, 2022
94c2bd4
fix: check client id in connection id verification
josecelano Sep 3, 2022
169228f
refactor: remove unused code
josecelano Sep 3, 2022
90a033d
refactor: move functions
josecelano Sep 3, 2022
cdf5cc6
refactor: extract struct Timestamp32
josecelano Sep 3, 2022
68b65b5
refactor: remove comment
josecelano Sep 5, 2022
09fa7cc
refactor: rename struct
josecelano Sep 5, 2022
0efd858
refactor: Timestamp32. Use trait for convertion
josecelano Sep 5, 2022
0fee0b5
feat: new CYpher trait and Blowfish impl
josecelano Sep 5, 2022
14e6e5e
refactor: extract struct Cypher
josecelano Sep 5, 2022
f46ce2f
refactor: rename function
josecelano Sep 5, 2022
02a1460
refactor: extract connection ID issuer
josecelano Sep 5, 2022
35819b4
refactor: extract struct ConnectionIdData
josecelano Sep 5, 2022
8eaefb1
refactor: extract struct EncryptedConnectionIdData
josecelano Sep 5, 2022
65c1b3b
fix: remove unneeded unwrap
josecelano Sep 5, 2022
dcf6d82
refactor: extract guard clauses for connection id verification
josecelano Sep 5, 2022
cd32b86
refactor: extract methods for EncryptedConnectionIdIssuer
josecelano Sep 5, 2022
4f01881
refactor: rename methods
josecelano Sep 6, 2022
b659d01
refactor: ClientId. Extract trait for hasher
josecelano Sep 6, 2022
1b98b15
refactor: remove unused hasher implemention for client id
josecelano Sep 6, 2022
460d62a
feat: verify connection id in announce and scrape requests
josecelano Sep 6, 2022
55c040d
refactor: simplify trait ConnectionIdIssuer
josecelano Sep 7, 2022
c891e83
feat: do not send response when connection id is not valid
josecelano Sep 7, 2022
75a2e5a
refactor: rename mod handlers to packet_handler
josecelano Sep 7, 2022
a2b38d4
refactor: extract mod request_handler
josecelano Sep 7, 2022
5801e5f
refactor: extract UDP RequestHandler
josecelano Sep 7, 2022
c3c31e5
refactor: moved RequestHandler creation to UdpServer::start()
mickvandijke Sep 8, 2022
f125781
refactor: renamed RequestHandler to PacketHandler
mickvandijke Sep 8, 2022
36e34ec
Merge pull request #78 from torrust/mick/protocol-tests
josecelano Sep 8, 2022
f70c11c
feat: generate a random encryption key
josecelano Sep 8, 2022
6c6c33d
refactor: use a tuple struct for Timestamp32
josecelano Sep 8, 2022
e793731
refactor: move method
josecelano Sep 8, 2022
90af290
refactor: extract EncodedConnectionIdData
josecelano Sep 8, 2022
84ae58c
refactor: rename methods
josecelano Sep 8, 2022
ce45c57
refactor: fix clippy warnings in upd::connection
josecelano Sep 9, 2022
9ad1ed7
refactor: inject clock into PackerHandler
josecelano Sep 9, 2022
0d45e20
refactor: rename PacketHandler attribtues
josecelano Sep 9, 2022
ab6a99f
refactor: replace deprecated crate for Blowfish cypher
josecelano Sep 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
330 changes: 325 additions & 5 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ opt-level = 3
lto = "fat"
strip = true

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "generate_connection_id"
harness = false

[dependencies]
tokio = { version = "1.7", features = ["rt-multi-thread", "net", "sync", "macros", "signal"] }

Expand All @@ -28,6 +35,10 @@ serde_with = "2.0.0"
hex = "0.4.3"
percent-encoding = "2.1.0"
binascii = "0.1"
blake3 = "1.3.1"
cipher = "0.4.3"
blowfish = "0.9.1"
byteorder = "1.4.3"

openssl = { version = "0.10.41", features = ["vendored"] }

Expand All @@ -52,3 +63,4 @@ async-trait = "0.1.52"

aquatic_udp_protocol = "0.2.0"
uuid = { version = "1.1.2", features = ["v4"] }
arraytools = "0.1.5"
38 changes: 38 additions & 0 deletions benches/generate_connection_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use aquatic_udp_protocol::ConnectionId;
use criterion::{Criterion, criterion_group, criterion_main};
use torrust_tracker::{udp::connection_id::get_connection_id, protocol::clock::current_timestamp};

fn get_connection_id_old(current_time: u64, port: u16) -> ConnectionId {
let time_i64 = (current_time / 3600) as i64;

ConnectionId((time_i64 | port as i64) << 36)
}

pub fn benchmark_generate_id_with_time_and_port(bench: &mut Criterion) {
let remote_address = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 117);
let current_time = current_timestamp();

bench.bench_function("generate_id_with_time_and_port", |b| {
b.iter(|| {
// Inner closure, the actual test
let _ = get_connection_id_old(current_time, remote_address.port());
})
});
}

pub fn benchmark_generate_id_with_hashed_time_and_ip_and_port_and_salt(bench: &mut Criterion) {
let remote_address = SocketAddr::from(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 117));
let current_time = current_timestamp();
let server_secret = [0;32];

bench.bench_function("generate_id_with_hashed_time_and_ip_and_port_and_salt", |b| {
b.iter(|| {
// Inner closure, the actual test
let _ = get_connection_id(&server_secret, &remote_address, current_time);
})
});
}

criterion_group!(benches, benchmark_generate_id_with_time_and_port, benchmark_generate_id_with_hashed_time_and_ip_and_port_and_salt);
criterion_main!(benches);
44 changes: 44 additions & 0 deletions src/api/server.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
//! # API Server
//!
//! HTTP server for the tracker HTTP API.
//!
//! Endpoint example:
//!
//! GET /api/torrent/:info_hash
//!
//! Get torrent details.
//!
//! ```s
//! curl -s http://127.0.0.1:1212/api/torrent/4beb7001cb833968582c67f55cc59dcc6c8d3fe5?token=MyAccessToken | jq
//! ```
//!
//! ```json
//! {
//! "info_hash": "4beb7001cb833968582c67f55cc59dcc6c8d3fe5",
//! "seeders": 1,
//! "completed": 0,
//! "leechers": 0,
//! "peers": [
//! {
//! "peer_id": {
//! "id": "2d7142343431302d7358376d33786d2877674179",
//! "client": "qBittorrent"
//! },
//! "peer_addr": "192.168.1.88:17548",
//! "updated": 385,
//! "uploaded": 0,
//! "downloaded": 0,
//! "left": 0,
//! "event": "None"
//! }
//! ]
//! }
//! ```
//!
//! | Parameter | Description |
//! |-----------|-------------|
//! | info_hash | The info_hash of the torrent. |
//!
//! The `info_hash.peers.updated` are the number of milliseconds since the last update.
//!

use std::cmp::min;
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
Expand Down
22 changes: 22 additions & 0 deletions src/protocol/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::time::SystemTime;

pub type UnixTime = u64;

/// A Clock which uses the UNIX time.
pub trait UnixClock {
fn now(&self) -> UnixTime;
}

/// A Clock which uses the operating system to determine the time.
pub struct SystemUnixClock;

impl UnixClock for SystemUnixClock {
fn now(&self) -> UnixTime {
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()
}
}

/// It returns the current timestamp using the system clock.
pub fn current_timestamp() -> UnixTime {
SystemUnixClock.now()
}
2 changes: 1 addition & 1 deletion src/protocol/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod common;
pub mod utils;
pub mod clock;
21 changes: 0 additions & 21 deletions src/protocol/utils.rs

This file was deleted.

7 changes: 3 additions & 4 deletions src/tracker/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use serde::Serialize;

use crate::protocol::utils::current_time;

use crate::AUTH_KEY_LENGTH;
use crate::protocol::clock::current_timestamp;

pub fn generate_auth_key(seconds_valid: u64) -> AuthKey {
let key: String = thread_rng()
Expand All @@ -19,12 +18,12 @@ pub fn generate_auth_key(seconds_valid: u64) -> AuthKey {

AuthKey {
key,
valid_until: Some(current_time() + seconds_valid),
valid_until: Some(current_timestamp() + seconds_valid),
}
}

pub fn verify_auth_key(auth_key: &AuthKey) -> Result<(), Error> {
let current_time = current_time();
let current_time = current_timestamp();
if auth_key.valid_until.is_none() { return Err(Error::KeyInvalid); }
if auth_key.valid_until.unwrap() < current_time { return Err(Error::KeyExpired); }

Expand Down
1 change: 1 addition & 0 deletions src/tracker/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod tracker;
pub mod statistics;
pub mod peer;
pub mod serializer;
pub mod torrent;
pub mod key;
pub mod mode;
2 changes: 1 addition & 1 deletion src/tracker/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use serde;
use serde::{Serialize};

use crate::protocol::common::{NumberOfBytesDef, AnnounceEventDef};
use crate::protocol::utils::ser_instant;
use crate::http::AnnounceRequest;
use crate::PeerId;
use crate::tracker::serializer::ser_instant;

#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
pub struct TorrentPeer {
Expand Down
47 changes: 47 additions & 0 deletions src/tracker/serializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// Serializer for `std::time::Instant` type.
/// Before serializing, it converts the instant to time elapse since that instant in milliseconds.
///
/// You can use it like this:
///
/// ```text
/// #[serde(serialize_with = "ser_instant")]
/// pub updated: std::time::Instant,
/// ```
///
pub fn ser_instant<S: serde::Serializer>(inst: &std::time::Instant, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_u64(inst.elapsed().as_millis() as u64)
}

#[cfg(test)]
mod tests {
use std::{time::Instant};
use serde::Serialize;

#[warn(unused_imports)]
use super::ser_instant;

#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
struct S {
#[serde(serialize_with = "ser_instant")]
pub time: Instant,
}

#[test]
fn instant_types_can_be_serialized_as_elapsed_time_since_that_instant_in_milliseconds() {

use std::{thread, time};

let t1 = Instant::now();

let s = S { time: t1 };

// Sleep 10 milliseconds
let ten_millis = time::Duration::from_millis(10);
thread::sleep(ten_millis);

let json_serialized_value = serde_json::to_string(&s).unwrap();

// Json contains time duration since t1 instant in milliseconds
assert_eq!(json_serialized_value, r#"{"time":10}"#);
}
}
104 changes: 104 additions & 0 deletions src/udp/connection/client_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//! ClientId is a unique ID for the UDP tracker client.
//! Currently implemented with a hash of the socket, i.e the IP and port.
use std::hash::Hash;
use std::hash::Hasher;
use std::net::SocketAddr;

#[derive(PartialEq, Debug, Clone)]
pub struct ClientId {
value: [u8; 4],
}

pub trait Make<T: Default + Hasher> {
fn new(socket: &SocketAddr) -> Self;

fn hash(socket: &SocketAddr) -> [u8;8] {
let mut hasher = T::default();
socket.hash(&mut hasher);

hasher.finish().to_le_bytes()
}
}

impl<T: Default + Hasher> Make<T> for ClientId {
fn new(socket: &SocketAddr) -> Self {
let hash = <ClientId as Make<T>>::hash(socket);

let mut truncated_hash: [u8; 4] = [0u8; 4];
truncated_hash.copy_from_slice(&hash[..4]);

ClientId {
value: truncated_hash,
}
}
}

impl ClientId {
/// It generates the ID with a previously generated value
pub fn from_bytes(bytes: &[u8]) -> Self {
let mut client_id = ClientId {
value: [0u8; 4]
};
client_id.value.copy_from_slice(bytes);
client_id
}

pub fn to_bytes(&self) -> [u8; 4] {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning &[u8; 4] or even &[u8] is preferred over [u8; 4], because borrowing is cheaper than creating a new owned array. Unless we want to consume self. In that case it should be:

pub fn to_bytes(self) -> [u8; 4] {
    self.value
}

The ClientId will then be dropped, because value is moved.

let bytes: [u8; 4] = self.value;
bytes
}
}

#[cfg(test)]
mod tests {
use std::{net::{IpAddr, Ipv4Addr, SocketAddr}, collections::hash_map::DefaultHasher};

use super::{ClientId, Make};

#[test]
fn it_should_be_a_hash_of_the_socket() {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let id: ClientId = Make::<DefaultHasher>::new(&socket);

assert_eq!(id.value, [213, 195, 130, 185]);
}

#[test]
fn id_should_be_converted_to_a_byte_array() {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let id: ClientId = Make::<DefaultHasher>::new(&socket);

assert_eq!(id.to_bytes(), [213, 195, 130, 185]);
}

#[test]
fn id_should_be_instantiate_from_a_previously_generated_value() {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let id: ClientId = Make::<DefaultHasher>::new(&socket);
let bytes = id.to_bytes();

assert_eq!(ClientId::from_bytes(&bytes), id);
}

#[test]
fn it_should_be_unique_with_different_socket_ips() {
let socket_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let socket_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8080);

assert_ne!(
<ClientId as Make::<DefaultHasher>>::new(&socket_1),
<ClientId as Make::<DefaultHasher>>::new(&socket_2)
);
}

#[test]
fn it_should_be_unique_with_different_socket_ports() {
let socket_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let socket_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081);

assert_ne!(
<ClientId as Make::<DefaultHasher>>::new(&socket_1),
<ClientId as Make::<DefaultHasher>>::new(&socket_2)
);
}
}
Loading