mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-04-02 10:45:30 +00:00
Add aquatic_peer_id crate for peer client parsing
This commit is contained in:
parent
73a903ed44
commit
3ca21390df
4 changed files with 195 additions and 0 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -179,6 +179,15 @@ dependencies = [
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aquatic_peer_id"
|
||||||
|
version = "0.8.0"
|
||||||
|
dependencies = [
|
||||||
|
"compact_str",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aquatic_toml_config"
|
name = "aquatic_toml_config"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ members = [
|
||||||
"aquatic_http",
|
"aquatic_http",
|
||||||
"aquatic_http_load_test",
|
"aquatic_http_load_test",
|
||||||
"aquatic_http_protocol",
|
"aquatic_http_protocol",
|
||||||
|
"aquatic_peer_id",
|
||||||
"aquatic_toml_config",
|
"aquatic_toml_config",
|
||||||
"aquatic_toml_config_derive",
|
"aquatic_toml_config_derive",
|
||||||
"aquatic_udp",
|
"aquatic_udp",
|
||||||
|
|
@ -29,6 +30,7 @@ rust-version = "1.64"
|
||||||
aquatic_common = { version = "0.8.0", path = "./aquatic_common" }
|
aquatic_common = { version = "0.8.0", path = "./aquatic_common" }
|
||||||
aquatic_http_protocol = { version = "0.8.0", path = "./aquatic_http_protocol" }
|
aquatic_http_protocol = { version = "0.8.0", path = "./aquatic_http_protocol" }
|
||||||
aquatic_http = { version = "0.8.0", path = "./aquatic_http" }
|
aquatic_http = { version = "0.8.0", path = "./aquatic_http" }
|
||||||
|
aquatic_peer_id = { version = "0.8.0", path = "./aquatic_peer_id" }
|
||||||
aquatic_toml_config = { version = "0.8.0", path = "./aquatic_toml_config" }
|
aquatic_toml_config = { version = "0.8.0", path = "./aquatic_toml_config" }
|
||||||
aquatic_toml_config_derive = { version = "0.8.0", path = "./aquatic_toml_config_derive" }
|
aquatic_toml_config_derive = { version = "0.8.0", path = "./aquatic_toml_config_derive" }
|
||||||
aquatic_udp_protocol = { version = "0.8.0", path = "./aquatic_udp_protocol" }
|
aquatic_udp_protocol = { version = "0.8.0", path = "./aquatic_udp_protocol" }
|
||||||
|
|
|
||||||
18
aquatic_peer_id/Cargo.toml
Normal file
18
aquatic_peer_id/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "aquatic_peer_id"
|
||||||
|
description = "BitTorrent peer ID handling"
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "aquatic_peer_id"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
compact_str = "0.7"
|
||||||
|
regex = "1"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
166
aquatic_peer_id/src/lib.rs
Normal file
166
aquatic_peer_id/src/lib.rs
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
use std::{fmt::Display, sync::OnceLock};
|
||||||
|
|
||||||
|
use compact_str::CompactString;
|
||||||
|
use regex::bytes::Regex;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct PeerId(pub [u8; 20]);
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum PeerClient {
|
||||||
|
BitTorrent(CompactString),
|
||||||
|
Deluge(CompactString),
|
||||||
|
LibTorrentRakshasa(CompactString),
|
||||||
|
LibTorrentRasterbar(CompactString),
|
||||||
|
QBitTorrent(CompactString),
|
||||||
|
Transmission(CompactString),
|
||||||
|
UTorrent(CompactString),
|
||||||
|
UTorrentEmbedded(CompactString),
|
||||||
|
UTorrentMac(CompactString),
|
||||||
|
UTorrentWeb(CompactString),
|
||||||
|
Vuze(CompactString),
|
||||||
|
WebTorrent(CompactString),
|
||||||
|
WebTorrentDesktop(CompactString),
|
||||||
|
Mainline(CompactString),
|
||||||
|
OtherWithPrefixAndVersion {
|
||||||
|
prefix: CompactString,
|
||||||
|
version: CompactString,
|
||||||
|
},
|
||||||
|
OtherWithPrefix(CompactString),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PeerClient {
|
||||||
|
pub fn from_prefix_and_version(prefix: &[u8], version: &[u8]) -> Option<Self> {
|
||||||
|
let version = CompactString::from_utf8(version).ok()?;
|
||||||
|
|
||||||
|
match prefix {
|
||||||
|
b"AZ" => Some(Self::Vuze(version)),
|
||||||
|
b"BT" => Some(Self::BitTorrent(version)),
|
||||||
|
b"DE" => Some(Self::Deluge(version)),
|
||||||
|
b"lt" => Some(Self::LibTorrentRakshasa(version)),
|
||||||
|
b"LT" => Some(Self::LibTorrentRasterbar(version)),
|
||||||
|
b"qB" => Some(Self::QBitTorrent(version)),
|
||||||
|
b"TR" => Some(Self::Transmission(version)),
|
||||||
|
b"UE" => Some(Self::UTorrentEmbedded(version)),
|
||||||
|
b"UM" => Some(Self::UTorrentMac(version)),
|
||||||
|
b"UT" => Some(Self::UTorrent(version)),
|
||||||
|
b"UW" => Some(Self::UTorrentWeb(version)),
|
||||||
|
b"WD" => Some(Self::WebTorrentDesktop(version)),
|
||||||
|
b"WW" => Some(Self::WebTorrent(version)),
|
||||||
|
b"M" => Some(Self::Mainline(version)),
|
||||||
|
name => Some(Self::OtherWithPrefixAndVersion {
|
||||||
|
prefix: CompactString::from_utf8(name).ok()?,
|
||||||
|
version,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_peer_id(peer_id: PeerId) -> Self {
|
||||||
|
static AZ_RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
|
||||||
|
if let Some(caps) = AZ_RE
|
||||||
|
.get_or_init(|| {
|
||||||
|
Regex::new(r"^\-(?P<name>[a-zA-Z]{2})(?P<version>[0-9A-Z]{4})")
|
||||||
|
.expect("compile AZ_RE regex")
|
||||||
|
})
|
||||||
|
.captures(&peer_id.0)
|
||||||
|
{
|
||||||
|
if let Some(client) = Self::from_prefix_and_version(&caps["name"], &caps["version"]) {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MAINLINE_RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
|
||||||
|
if let Some(caps) = MAINLINE_RE
|
||||||
|
.get_or_init(|| {
|
||||||
|
Regex::new(r"^(?P<name>[a-zA-Z])(?P<version>[0-9\-]{6})\-")
|
||||||
|
.expect("compile MAINLINE_RE regex")
|
||||||
|
})
|
||||||
|
.captures(&peer_id.0)
|
||||||
|
{
|
||||||
|
if let Some(client) = Self::from_prefix_and_version(&caps["name"], &caps["version"]) {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PREFIX_RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
|
||||||
|
if let Some(caps) = PREFIX_RE
|
||||||
|
.get_or_init(|| {
|
||||||
|
Regex::new(r"^(?P<prefix>[a-zA-Z0-9\-]*)\-").expect("compile PREFIX_RE regex")
|
||||||
|
})
|
||||||
|
.captures(&peer_id.0)
|
||||||
|
{
|
||||||
|
if let Ok(prefix) = CompactString::from_utf8(&caps["prefix"]) {
|
||||||
|
return Self::OtherWithPrefix(prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PeerClient {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::BitTorrent(v) => write!(f, "BitTorrent ({})", v.as_str()),
|
||||||
|
Self::Deluge(v) => write!(f, "Deluge ({})", v.as_str()),
|
||||||
|
Self::LibTorrentRakshasa(v) => write!(f, "libTorrent (rakshasa) ({})", v.as_str()),
|
||||||
|
Self::LibTorrentRasterbar(v) => write!(f, "libtorrent (rasterbar) ({})", v.as_str()),
|
||||||
|
Self::QBitTorrent(v) => write!(f, "QBitTorrent ({})", v.as_str()),
|
||||||
|
Self::Transmission(v) => write!(f, "Transmission ({})", v.as_str()),
|
||||||
|
Self::UTorrent(v) => write!(f, "uTorrent ({})", v.as_str()),
|
||||||
|
Self::UTorrentEmbedded(v) => write!(f, "uTorrent Embedded ({})", v.as_str()),
|
||||||
|
Self::UTorrentMac(v) => write!(f, "uTorrent Mac ({})", v.as_str()),
|
||||||
|
Self::UTorrentWeb(v) => write!(f, "uTorrent Web ({})", v.as_str()),
|
||||||
|
Self::Vuze(v) => write!(f, "Vuze ({})", v.as_str()),
|
||||||
|
Self::WebTorrent(v) => write!(f, "WebTorrent ({})", v.as_str()),
|
||||||
|
Self::WebTorrentDesktop(v) => write!(f, "WebTorrent Desktop ({})", v.as_str()),
|
||||||
|
Self::Mainline(v) => write!(f, "Mainline ({})", v.as_str()),
|
||||||
|
Self::OtherWithPrefixAndVersion { prefix, version } => {
|
||||||
|
write!(f, "Other ({}) ({})", prefix.as_str(), version.as_str())
|
||||||
|
}
|
||||||
|
Self::OtherWithPrefix(prefix) => write!(f, "Other ({})", prefix.as_str()),
|
||||||
|
Self::Other => f.write_str("Other"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_peer_id(bytes: &[u8]) -> PeerId {
|
||||||
|
let mut peer_id = PeerId([0; 20]);
|
||||||
|
|
||||||
|
let len = bytes.len();
|
||||||
|
|
||||||
|
(&mut peer_id.0[..len]).copy_from_slice(bytes);
|
||||||
|
|
||||||
|
peer_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_client_from_peer_id() {
|
||||||
|
assert_eq!(
|
||||||
|
PeerClient::from_peer_id(create_peer_id(b"-lt1234-k/asdh3")),
|
||||||
|
PeerClient::LibTorrentRakshasa("1234".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
PeerClient::from_peer_id(create_peer_id(b"M1-2-3--k/asdh3")),
|
||||||
|
PeerClient::Mainline("1-2-3-".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
PeerClient::from_peer_id(create_peer_id(b"M1-23-4-k/asdh3")),
|
||||||
|
PeerClient::Mainline("1-23-4".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
PeerClient::from_peer_id(create_peer_id(b"S3-k/asdh3")),
|
||||||
|
PeerClient::OtherWithPrefix("S3".into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue