mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-03-31 17:55:36 +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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aquatic_peer_id"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"regex",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aquatic_toml_config"
|
||||
version = "0.8.0"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ members = [
|
|||
"aquatic_http",
|
||||
"aquatic_http_load_test",
|
||||
"aquatic_http_protocol",
|
||||
"aquatic_peer_id",
|
||||
"aquatic_toml_config",
|
||||
"aquatic_toml_config_derive",
|
||||
"aquatic_udp",
|
||||
|
|
@ -29,6 +30,7 @@ rust-version = "1.64"
|
|||
aquatic_common = { version = "0.8.0", path = "./aquatic_common" }
|
||||
aquatic_http_protocol = { version = "0.8.0", path = "./aquatic_http_protocol" }
|
||||
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_derive = { version = "0.8.0", path = "./aquatic_toml_config_derive" }
|
||||
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