Merge pull request #374 from av-gal/main
Include crate version in client fingerprint portion of `peer_id`
This commit is contained in:
commit
3fa55bdc14
13 changed files with 170 additions and 24 deletions
|
|
@ -38,7 +38,7 @@ rand = "0.8"
|
||||||
indexmap = "2"
|
indexmap = "2"
|
||||||
dashmap = { version = "6", features = ["serde"] }
|
dashmap = { version = "6", features = ["serde"] }
|
||||||
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
||||||
librqbit-core = { path = "../librqbit_core", default-features = false, version = "4.1" }
|
librqbit-core = { path = "../librqbit_core", default-features = false, version = "5" }
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
tokio-util = "0.7.10"
|
tokio-util = "0.7.10"
|
||||||
bytes = "1.7.1"
|
bytes = "1.7.1"
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,9 @@ use futures::{
|
||||||
|
|
||||||
use leaky_bucket::RateLimiter;
|
use leaky_bucket::RateLimiter;
|
||||||
use librqbit_core::{
|
use librqbit_core::{
|
||||||
|
crate_version,
|
||||||
hash_id::Id20,
|
hash_id::Id20,
|
||||||
peer_id::generate_peer_id,
|
peer_id::generate_azereus_style,
|
||||||
spawn_utils::{spawn, spawn_with_cancel},
|
spawn_utils::{spawn, spawn_with_cancel},
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
@ -1167,7 +1168,9 @@ impl DhtState {
|
||||||
.context("cannot determine UDP listen addr")?;
|
.context("cannot determine UDP listen addr")?;
|
||||||
info!("DHT listening on {:?}", listen_addr);
|
info!("DHT listening on {:?}", listen_addr);
|
||||||
|
|
||||||
let peer_id = config.peer_id.unwrap_or_else(generate_peer_id);
|
let peer_id = config
|
||||||
|
.peer_id
|
||||||
|
.unwrap_or_else(|| (generate_azereus_style(*b"rQ", crate_version!())));
|
||||||
info!("starting up DHT with peer id {:?}", peer_id);
|
info!("starting up DHT with peer id {:?}", peer_id);
|
||||||
let bootstrap_addrs = config
|
let bootstrap_addrs = config
|
||||||
.bootstrap_addrs
|
.bootstrap_addrs
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ home = { version = "=0.5.5", optional = true }
|
||||||
bencode = { path = "../bencode", default-features = false, package = "librqbit-bencode", version = "3" }
|
bencode = { path = "../bencode", default-features = false, package = "librqbit-bencode", version = "3" }
|
||||||
tracker_comms = { path = "../tracker_comms", default-features = false, package = "librqbit-tracker-comms", version = "2.1" }
|
tracker_comms = { path = "../tracker_comms", default-features = false, package = "librqbit-tracker-comms", version = "2.1" }
|
||||||
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
||||||
librqbit-core = { path = "../librqbit_core", default-features = false, version = "4.1" }
|
librqbit-core = { path = "../librqbit_core", default-features = false, version = "5" }
|
||||||
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
||||||
peer_binary_protocol = { path = "../peer_binary_protocol", default-features = false, package = "librqbit-peer-protocol", version = "4.2" }
|
peer_binary_protocol = { path = "../peer_binary_protocol", default-features = false, package = "librqbit-peer-protocol", version = "4.2" }
|
||||||
sha1w = { path = "../sha1w", default-features = false, package = "librqbit-sha1-wrapper", version = "4.1" }
|
sha1w = { path = "../sha1w", default-features = false, package = "librqbit-sha1-wrapper", version = "4.1" }
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ mod tests {
|
||||||
let dht = DhtBuilder::new().await.unwrap();
|
let dht = DhtBuilder::new().await.unwrap();
|
||||||
|
|
||||||
let peer_rx = dht.get_peers(info_hash, None);
|
let peer_rx = dht.get_peers(info_hash, None);
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id(b"-xx1234-");
|
||||||
match read_metainfo_from_peer_receiver(
|
match read_metainfo_from_peer_receiver(
|
||||||
peer_id,
|
peer_id,
|
||||||
info_hash,
|
info_hash,
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ mod tests {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let addr = SocketAddr::from_str("127.0.0.1:27311").unwrap();
|
let addr = SocketAddr::from_str("127.0.0.1:27311").unwrap();
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id(b"-xx1234-");
|
||||||
let info_hash = Id20::from_str("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
|
let info_hash = Id20::from_str("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
|
||||||
dbg!(read_metainfo_from_peer(
|
dbg!(read_metainfo_from_peer(
|
||||||
addr,
|
addr,
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,10 @@ use futures::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use librqbit_core::{
|
use librqbit_core::{
|
||||||
constants::CHUNK_SIZE,
|
constants::CHUNK_SIZE,
|
||||||
|
crate_version,
|
||||||
directories::get_configuration_directory,
|
directories::get_configuration_directory,
|
||||||
magnet::Magnet,
|
magnet::Magnet,
|
||||||
peer_id::generate_peer_id,
|
peer_id::generate_azereus_style,
|
||||||
spawn_utils::spawn_with_cancel,
|
spawn_utils::spawn_with_cancel,
|
||||||
torrent_metainfo::{TorrentMetaV1Info, TorrentMetaV1Owned},
|
torrent_metainfo::{TorrentMetaV1Info, TorrentMetaV1Owned},
|
||||||
};
|
};
|
||||||
|
|
@ -498,7 +499,9 @@ impl Session {
|
||||||
mut opts: SessionOptions,
|
mut opts: SessionOptions,
|
||||||
) -> BoxFuture<'static, anyhow::Result<Arc<Self>>> {
|
) -> BoxFuture<'static, anyhow::Result<Arc<Self>>> {
|
||||||
async move {
|
async move {
|
||||||
let peer_id = opts.peer_id.unwrap_or_else(generate_peer_id);
|
let peer_id = opts
|
||||||
|
.peer_id
|
||||||
|
.unwrap_or_else(|| generate_azereus_style(*b"rQ", crate_version!()));
|
||||||
let token = opts.cancellation_token.take().unwrap_or_default();
|
let token = opts.cancellation_token.take().unwrap_or_default();
|
||||||
|
|
||||||
#[cfg(feature = "disable-upload")]
|
#[cfg(feature = "disable-upload")]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "librqbit-core"
|
name = "librqbit-core"
|
||||||
version = "4.1.0"
|
version = "5.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Important utilities used throughout librqbit useful for working with torrents."
|
description = "Important utilities used throughout librqbit useful for working with torrents."
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
@ -20,7 +20,7 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
url = { version = "2", default-features = false }
|
url = { version = "2", default-features = false }
|
||||||
uuid = { version = "1", features = ["v4"] }
|
rand = "0.8"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,18 @@
|
||||||
use crate::hash_id::Id20;
|
use crate::hash_id::Id20;
|
||||||
|
use rand::{self, RngCore};
|
||||||
|
|
||||||
|
/// Return the version of the invoking crate as a tuple
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! crate_version {
|
||||||
|
() => {
|
||||||
|
(
|
||||||
|
env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap_or(0),
|
||||||
|
env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap_or(0),
|
||||||
|
env!("CARGO_PKG_VERSION_PATCH").parse::<u8>().unwrap_or(0),
|
||||||
|
env!("CARGO_PKG_VERSION_PRE").parse::<u8>().unwrap_or(0),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AzureusStyleKind {
|
pub enum AzureusStyleKind {
|
||||||
|
|
@ -8,13 +22,13 @@ pub enum AzureusStyleKind {
|
||||||
QBittorrent,
|
QBittorrent,
|
||||||
UTorrent,
|
UTorrent,
|
||||||
RQBit,
|
RQBit,
|
||||||
Other([char; 2]),
|
Other([u8; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AzureusStyle {
|
pub struct AzureusStyle {
|
||||||
pub kind: AzureusStyleKind,
|
pub kind: AzureusStyleKind,
|
||||||
pub version: [char; 4],
|
pub version: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AzureusStyleKind {
|
impl AzureusStyleKind {
|
||||||
|
|
@ -26,7 +40,7 @@ impl AzureusStyleKind {
|
||||||
b"qB" => AzureusStyleKind::QBittorrent,
|
b"qB" => AzureusStyleKind::QBittorrent,
|
||||||
b"UT" => AzureusStyleKind::UTorrent,
|
b"UT" => AzureusStyleKind::UTorrent,
|
||||||
b"rQ" => AzureusStyleKind::RQBit,
|
b"rQ" => AzureusStyleKind::RQBit,
|
||||||
_ => AzureusStyleKind::Other([b1 as char, b2 as char]),
|
other => AzureusStyleKind::Other(*other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,9 +50,9 @@ fn try_decode_azureus_style(p: &Id20) -> Option<AzureusStyle> {
|
||||||
if !(p[0] == b'-' && p[7] == b'-') {
|
if !(p[0] == b'-' && p[7] == b'-') {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut version = ['0'; 4];
|
let mut version = [b'0'; 4];
|
||||||
for (i, c) in p[3..7].iter().copied().enumerate() {
|
for (i, c) in p[3..7].iter().copied().enumerate() {
|
||||||
version[i] = c as char;
|
version[i] = version_digit_from_id(c)?;
|
||||||
}
|
}
|
||||||
let kind = AzureusStyleKind::from_bytes(p[1], p[2]);
|
let kind = AzureusStyleKind::from_bytes(p[1], p[2]);
|
||||||
Some(AzureusStyle { kind, version })
|
Some(AzureusStyle { kind, version })
|
||||||
|
|
@ -53,13 +67,62 @@ pub fn try_decode_peer_id(p: Id20) -> Option<PeerId> {
|
||||||
Some(PeerId::AzureusStyle(try_decode_azureus_style(&p)?))
|
Some(PeerId::AzureusStyle(try_decode_azureus_style(&p)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_peer_id() -> Id20 {
|
/// Returns `None` for bytes greater than 64
|
||||||
|
fn version_digit_to_id(d: u8) -> Option<u8> {
|
||||||
|
let version_map = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-";
|
||||||
|
version_map.get(d as usize).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `None` for bytes that aren't alphanumeric, `.` or `-`.
|
||||||
|
fn version_digit_from_id(d: u8) -> Option<u8> {
|
||||||
|
match d {
|
||||||
|
b'0'..=b'9' => Some(d - b'0'),
|
||||||
|
b'A'..=b'Z' => Some(d - b'0' - 10),
|
||||||
|
b'a'..=b'z' => Some(d - b'0' - 10 - 26),
|
||||||
|
b'.' => Some(62),
|
||||||
|
b'-' => Some(63),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a client fingerprint in the Azereus format, where `b"-xx1234-"` corresponds to version `1.2.3.4`` of the torrent client abbreviated by `xx`
|
||||||
|
pub fn generate_azereus_style(client: [u8; 2], version: (u8, u8, u8, u8)) -> Id20 {
|
||||||
|
let mut fingerprint = [b'-'; 8];
|
||||||
|
|
||||||
|
fingerprint[1..3].copy_from_slice(&client);
|
||||||
|
fingerprint[3] = version_digit_to_id(version.0).unwrap();
|
||||||
|
fingerprint[4] = version_digit_to_id(version.1).unwrap();
|
||||||
|
fingerprint[5] = version_digit_to_id(version.2).unwrap();
|
||||||
|
fingerprint[6] = version_digit_to_id(version.3).unwrap();
|
||||||
|
generate_peer_id(&fingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics if the `fingerprint` slice isn't eight bytes long
|
||||||
|
pub fn generate_peer_id(fingerprint: &[u8]) -> Id20 {
|
||||||
let mut peer_id = [0u8; 20];
|
let mut peer_id = [0u8; 20];
|
||||||
|
|
||||||
let u = uuid::Uuid::new_v4();
|
peer_id[..8].copy_from_slice(fingerprint);
|
||||||
peer_id[4..20].copy_from_slice(&u.as_bytes()[..]);
|
rand::thread_rng().fill_bytes(&mut peer_id[8..]);
|
||||||
|
|
||||||
peer_id[..8].copy_from_slice(b"-rQ7000-");
|
|
||||||
|
|
||||||
Id20::new(peer_id)
|
Id20::new(peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::peer_id::generate_azereus_style;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_azereus_peer_id_generation() {
|
||||||
|
for (client, version, correct_fingerprint) in [
|
||||||
|
(*b"xx", (1, 2, 3, 4), *b"-xx1234-"),
|
||||||
|
(*b"00", (10, 0, 0, 0), *b"-00A000-"),
|
||||||
|
(*b"\xFF\xFF", (36, 37, 62, 63), *b"-\xFF\xFFab.--"),
|
||||||
|
] {
|
||||||
|
let id1 = generate_azereus_style(client, version);
|
||||||
|
let id2 = generate_azereus_style(client, version);
|
||||||
|
assert_ne!(id1, id2);
|
||||||
|
assert_eq!(id1.0[..8], id2.0[..8]);
|
||||||
|
assert_eq!(id1.0[..8], correct_fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ byteorder = "1"
|
||||||
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
||||||
bencode = { path = "../bencode", default-features = false, package = "librqbit-bencode", version = "3" }
|
bencode = { path = "../bencode", default-features = false, package = "librqbit-bencode", version = "3" }
|
||||||
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
clone_to_owned = { path = "../clone_to_owned", package = "librqbit-clone-to-owned", version = "3" }
|
||||||
librqbit-core = { path = "../librqbit_core", default-features = false, version = "4.1" }
|
librqbit-core = { path = "../librqbit_core", default-features = false, version = "5" }
|
||||||
bitvec = "1"
|
bitvec = "1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
bytes = "1.7.1"
|
bytes = "1.7.1"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ anyhow = "1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
async-stream = "0.3.5"
|
async-stream = "0.3.5"
|
||||||
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
buffers = { path = "../buffers", package = "librqbit-buffers", version = "4.2" }
|
||||||
librqbit-core = { path = "../librqbit_core", default-features = false, version = "4.1" }
|
librqbit-core = { path = "../librqbit_core", default-features = false, version = "5" }
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
|
|
|
||||||
|
|
@ -447,7 +447,7 @@ mod tests {
|
||||||
connection_id,
|
connection_id,
|
||||||
AnnounceFields {
|
AnnounceFields {
|
||||||
info_hash: hash,
|
info_hash: hash,
|
||||||
peer_id: generate_peer_id(),
|
peer_id: generate_peer_id(b"-xx1234-"),
|
||||||
downloaded: 0,
|
downloaded: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
uploaded: 0,
|
uploaded: 0,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ uuid = { version = "1.10.0", features = ["v4"] }
|
||||||
librqbit-upnp = { version = "1", path = "../upnp", default-features = false }
|
librqbit-upnp = { version = "1", path = "../upnp", default-features = false }
|
||||||
gethostname = "0.5.0"
|
gethostname = "0.5.0"
|
||||||
librqbit-sha1-wrapper = { path = "../sha1w", version = "4", default-features = false }
|
librqbit-sha1-wrapper = { path = "../sha1w", version = "4", default-features = false }
|
||||||
librqbit-core = { version = "4.1", path = "../librqbit_core", default-features = false }
|
librqbit-core = { version = "5", path = "../librqbit_core", default-features = false }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
url = { version = "2", default-features = false }
|
url = { version = "2", default-features = false }
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
|
|
|
||||||
77
flake.lock
generated
Normal file
77
flake.lock
generated
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1748460212,
|
||||||
|
"narHash": "sha256-RBUseGlYAKOd8hnKVujiGzpdJoZWj5e3A+Ds2mKsv28=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "88e58d66efad1b3e0edf8633ea0774f7105d37c9",
|
||||||
|
"revCount": 86,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.lix.systems/lix-project/flake-compat"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.lix.systems/lix-project/flake-compat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1748302896,
|
||||||
|
"narHash": "sha256-ixMT0a8mM091vSswlTORZj93WQAJsRNmEvqLL+qwTFM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "7848cd8c982f7740edf76ddb3b43d234cb80fc4d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue