add detailed peer listing in webserver

This commit is contained in:
Naim A 2020-05-07 04:27:16 +03:00
parent e86e2c367f
commit ad369d1d22
3 changed files with 144 additions and 8 deletions

View file

@ -210,11 +210,13 @@ impl UDPTracker {
let client_addr = SocketAddr::new(remote_addr.ip(), packet.port);
let info_hash = packet.info_hash.into();
let peer_id: &tracker::PeerId = tracker::PeerId::from_array(&packet.peer_id);
match self
.tracker
.update_torrent_and_get_stats(
&info_hash,
&packet.peer_id,
peer_id,
&client_addr,
packet.uploaded,
packet.downloaded,

View file

@ -25,16 +25,21 @@ pub enum TrackerMode {
PrivateMode,
}
#[derive(Clone)]
struct TorrentPeer {
#[derive(Clone, Serialize)]
pub struct TorrentPeer {
ip: std::net::SocketAddr,
uploaded: u64,
downloaded: u64,
left: u64,
event: Events,
#[serde(serialize_with = "ser_instant")]
updated: std::time::Instant,
}
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)
}
#[derive(Ord, PartialEq, Eq, Clone)]
pub struct InfoHash {
info_hash: [u8; 20],
@ -128,7 +133,112 @@ impl<'de> serde::de::Deserialize<'de> for InfoHash {
}
}
pub type PeerId = [u8; 20];
#[repr(transparent)]
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
pub struct PeerId([u8; 20]);
impl PeerId {
pub fn from_array(v: &[u8; 20]) -> &PeerId {
unsafe {
// This is safe since PeerId's repr is transparent and content's are identical. PeerId == [0u8; 20]
core::mem::transmute(v)
}
}
pub fn get_client_name(&self) -> Option<&'static str> {
if self.0[0] == b'M' {
return Some("BitTorrent");
}
if self.0[0] == b'-' {
let name = match &self.0[1..3] {
b"AG" => "Ares",
b"A~" => "Ares",
b"AR" => "Arctic",
b"AV" => "Avicora",
b"AX" => "BitPump",
b"AZ" => "Azureus",
b"BB" => "BitBuddy",
b"BC" => "BitComet",
b"BF" => "Bitflu",
b"BG" => "BTG (uses Rasterbar libtorrent)",
b"BR" => "BitRocket",
b"BS" => "BTSlave",
b"BX" => "~Bittorrent X",
b"CD" => "Enhanced CTorrent",
b"CT" => "CTorrent",
b"DE" => "DelugeTorrent",
b"DP" => "Propagate Data Client",
b"EB" => "EBit",
b"ES" => "electric sheep",
b"FT" => "FoxTorrent",
b"FW" => "FrostWire",
b"FX" => "Freebox BitTorrent",
b"GS" => "GSTorrent",
b"HL" => "Halite",
b"HN" => "Hydranode",
b"KG" => "KGet",
b"KT" => "KTorrent",
b"LH" => "LH-ABC",
b"LP" => "Lphant",
b"LT" => "libtorrent",
b"lt" => "libTorrent",
b"LW" => "LimeWire",
b"MO" => "MonoTorrent",
b"MP" => "MooPolice",
b"MR" => "Miro",
b"MT" => "MoonlightTorrent",
b"NX" => "Net Transport",
b"PD" => "Pando",
b"qB" => "qBittorrent",
b"QD" => "QQDownload",
b"QT" => "Qt 4 Torrent example",
b"RT" => "Retriever",
b"S~" => "Shareaza alpha/beta",
b"SB" => "~Swiftbit",
b"SS" => "SwarmScope",
b"ST" => "SymTorrent",
b"st" => "sharktorrent",
b"SZ" => "Shareaza",
b"TN" => "TorrentDotNET",
b"TR" => "Transmission",
b"TS" => "Torrentstorm",
b"TT" => "TuoTu",
b"UL" => "uLeecher!",
b"UT" => "µTorrent",
b"UW" => "µTorrent Web",
b"VG" => "Vagaa",
b"WD" => "WebTorrent Desktop",
b"WT" => "BitLet",
b"WW" => "WebTorrent",
b"WY" => "FireTorrent",
b"XL" => "Xunlei",
b"XT" => "XanTorrent",
b"XX" => "Xtorrent",
b"ZT" => "ZipTorrent",
_ => return None,
};
Some(name)
} else {
None
}
}
}
impl Serialize for PeerId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
let mut tmp = [0u8; 40];
binascii::bin2hex(&self.0, &mut tmp).unwrap();
let id = std::str::from_utf8(&tmp).ok();
#[derive(Serialize)]
struct PeerIdInfo<'a> {
id: Option<&'a str>,
client: Option<&'a str>,
}
let obj = PeerIdInfo { id, client: self.get_client_name() };
obj.serialize(serializer)
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct TorrentEntry {
@ -208,6 +318,10 @@ impl TorrentEntry {
list
}
pub fn get_peers_iter(&self) -> impl Iterator<Item=(&PeerId, &TorrentPeer)> {
self.peers.iter()
}
pub fn get_stats(&self) -> (u32, u32, u32) {
let leechers = (self.peers.len() as u32) - self.seeders;
(self.seeders, self.completed, leechers)

View file

@ -31,6 +31,11 @@ struct TorrentEntry<'a> {
info_hash: &'a InfoHash,
#[serde(flatten)]
data: &'a crate::tracker::TorrentEntry,
seeders: u32,
leechers: u32,
#[serde(skip_serializing_if="Option::is_none")]
peers: Option<Vec<(crate::tracker::PeerId, crate::tracker::TorrentPeer)>>,
}
#[derive(Serialize, Deserialize)]
@ -101,7 +106,10 @@ pub fn build_server(
let db = tracker.get_database().await;
let results: Vec<_> = db
.iter()
.map(|(k, v)| TorrentEntry { info_hash: k, data: v })
.map(|(k, v)| {
let (seeders, _, leechers) = v.get_stats();
TorrentEntry { info_hash: k, data: v, seeders, leechers, peers: None }
})
.skip(offset as usize)
.take(limit as usize)
.collect();
@ -113,7 +121,7 @@ pub fn build_server(
let t2 = tracker.clone();
// view_torrent_info -> GET /t/:infohash HTTP/*
let view_torrent_info = filters::method::get()
.and(filters::path::param())
.and(filters::path::param()).and(filters::path::end())
.map(move |info_hash: InfoHash| {
let tracker = t2.clone();
(info_hash, tracker)
@ -125,10 +133,22 @@ pub fn build_server(
Some(v) => v,
None => return Err(warp::reject::reject()),
};
let (seeders, _, leechers) = info.get_stats();
let peers: Vec<_> = info
.get_peers_iter()
.take(1000)
.map(|(peer_id, peer_info)| {
(peer_id.clone(), peer_info.clone())
})
.collect();
Ok(reply::json(&TorrentEntry {
info_hash: &info_hash,
data: info,
seeders,
leechers,
peers: Some(peers),
}))
}
});
@ -136,7 +156,7 @@ pub fn build_server(
// DELETE /t/:info_hash
let t3 = tracker.clone();
let delete_torrent = filters::method::delete()
.and(filters::path::param())
.and(filters::path::param()).and(filters::path::end())
.map(move |info_hash: InfoHash| {
let tracker = t3.clone();
(info_hash, tracker)
@ -160,7 +180,7 @@ pub fn build_server(
// add_torrent/alter: POST /t/:info_hash
// (optional) BODY: json: {"is_flagged": boolean}
let change_torrent = filters::method::post()
.and(filters::path::param())
.and(filters::path::param()).and(filters::path::end())
.and(filters::body::content_length_limit(4096))
.and(filters::body::json())
.map(move |info_hash: InfoHash, body: Option<TorrentFlag>| {