Skip to content

Commit e9e992f

Browse files
committed
fix: correctly decoding peer_id from http announce request
1 parent 443b888 commit e9e992f

File tree

6 files changed

+70
-21
lines changed

6 files changed

+70
-21
lines changed

src/common.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,23 @@ impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
126126
}
127127

128128
#[derive(PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
129-
pub struct PeerId(pub String);
129+
pub struct PeerId(pub [u8; 20]);
130+
131+
impl PeerId {
132+
pub fn to_string(&self) -> String {
133+
let mut buffer = [0u8; 20];
134+
let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok().unwrap();
135+
String::from(std::str::from_utf8(bytes_out).unwrap())
136+
}
137+
}
130138

131139
impl PeerId {
132140
pub fn get_client_name(&self) -> Option<&'static str> {
133-
if self.0.as_bytes()[0] == b'M' {
141+
if self.0[0] == b'M' {
134142
return Some("BitTorrent");
135143
}
136-
if self.0.as_bytes()[0] == b'-' {
137-
let name = match &self.0.as_bytes()[1..3] {
144+
if self.0[0] == b'-' {
145+
let name = match &self.0[1..3] {
138146
b"AG" => "Ares",
139147
b"A~" => "Ares",
140148
b"AR" => "Arctic",
@@ -211,9 +219,9 @@ impl Serialize for PeerId {
211219
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
212220
where
213221
S: serde::Serializer, {
214-
let buff_size = self.0.as_bytes().len() * 2;
222+
let buff_size = self.0.len() * 2;
215223
let mut tmp: Vec<u8> = vec![0; buff_size];
216-
binascii::bin2hex(&self.0.as_bytes(), &mut tmp).unwrap();
224+
binascii::bin2hex(&self.0, &mut tmp).unwrap();
217225
let id = std::str::from_utf8(&tmp).ok();
218226

219227
#[derive(Serialize)]

src/torrust_http_tracker/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ pub enum ServerError {
99
#[error("info_hash is either missing or invalid")]
1010
InvalidInfoHash,
1111

12+
#[error("peer_id is either missing or invalid")]
13+
InvalidPeerId,
14+
1215
#[error("could not find remote address")]
1316
AddressNotFound,
1417

src/torrust_http_tracker/filters.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::net::{IpAddr, SocketAddr};
33
use std::str::FromStr;
44
use std::sync::Arc;
55
use warp::{Filter, reject, Rejection};
6-
use crate::{InfoHash, MAX_SCRAPE_TORRENTS, TorrentTracker};
6+
use crate::{InfoHash, MAX_SCRAPE_TORRENTS, PeerId, TorrentTracker};
77
use crate::key_manager::AuthKey;
88
use crate::torrust_http_tracker::{AnnounceRequest, AnnounceRequestQuery, ScrapeRequest, ServerError, WebResult};
99

@@ -44,6 +44,49 @@ async fn info_hashes(raw_query: String) -> WebResult<Vec<InfoHash>> {
4444
}
4545
}
4646

47+
/// Check for PeerId
48+
pub fn with_peer_id() -> impl Filter<Extract = (PeerId,), Error = Rejection> + Clone {
49+
warp::filters::query::raw()
50+
.and_then(peer_id)
51+
}
52+
53+
/// Parse PeerId from raw query string
54+
async fn peer_id(raw_query: String) -> WebResult<PeerId> {
55+
// put all query params in a vec
56+
let split_raw_query: Vec<&str> = raw_query.split("&").collect();
57+
58+
let mut peer_id: Option<PeerId> = None;
59+
60+
for v in split_raw_query {
61+
// look for the peer_id param
62+
if v.contains("peer_id") {
63+
// get raw percent_encoded peer_id
64+
let raw_peer_id = v.split("=").collect::<Vec<&str>>()[1];
65+
66+
// decode peer_id
67+
let peer_id_bytes = percent_encoding::percent_decode_str(raw_peer_id).collect::<Vec<u8>>();
68+
69+
// peer_id must be 20 bytes
70+
if peer_id_bytes.len() > 20 {
71+
return Err(reject::custom(ServerError::InvalidPeerId));
72+
}
73+
74+
// clone peer_id_bytes into fixed length array
75+
let mut byte_arr: [u8; 20] = Default::default();
76+
byte_arr.clone_from_slice(peer_id_bytes.as_slice());
77+
78+
peer_id = Some(PeerId(byte_arr));
79+
break;
80+
}
81+
}
82+
83+
if peer_id.is_none() {
84+
Err(reject::custom(ServerError::InvalidPeerId))
85+
} else {
86+
Ok(peer_id.unwrap())
87+
}
88+
}
89+
4790
/// Pass Arc<TorrentTracker> along
4891
pub fn with_auth_key() -> impl Filter<Extract = (Option<AuthKey>,), Error = Infallible> + Clone {
4992
warp::path::param::<String>()
@@ -59,13 +102,14 @@ pub fn with_auth_key() -> impl Filter<Extract = (Option<AuthKey>,), Error = Infa
59102
pub fn with_announce_request() -> impl Filter<Extract = (AnnounceRequest,), Error = Rejection> + Clone {
60103
warp::filters::query::query::<AnnounceRequestQuery>()
61104
.and(with_info_hash())
105+
.and(with_peer_id())
62106
.and(warp::addr::remote())
63107
.and(warp::header::optional::<String>("X-Forwarded-For"))
64108
.and_then(announce_request)
65109
}
66110

67111
/// Parse AnnounceRequest from raw AnnounceRequestQuery, InfoHash and Option<SocketAddr>
68-
async fn announce_request(announce_request_query: AnnounceRequestQuery, info_hashes: Vec<InfoHash>, remote_addr: Option<SocketAddr>, forwarded_for: Option<String>) -> WebResult<AnnounceRequest> {
112+
async fn announce_request(announce_request_query: AnnounceRequestQuery, info_hashes: Vec<InfoHash>, peer_id: PeerId, remote_addr: Option<SocketAddr>, forwarded_for: Option<String>) -> WebResult<AnnounceRequest> {
69113
if remote_addr.is_none() { return Err(reject::custom(ServerError::AddressNotFound)) }
70114

71115
// get first forwarded ip
@@ -76,14 +120,14 @@ async fn announce_request(announce_request_query: AnnounceRequestQuery, info_has
76120
.and_then(|ip_str| IpAddr::from_str(ip_str).ok())
77121
}
78122
};
79-
123+
80124
Ok(AnnounceRequest {
81125
info_hash: info_hashes[0],
82126
peer_addr: remote_addr.unwrap(),
83127
forwarded_ip,
84128
downloaded: announce_request_query.downloaded,
85129
uploaded: announce_request_query.uploaded,
86-
peer_id: announce_request_query.peer_id,
130+
peer_id,
87131
port: announce_request_query.port,
88132
left: announce_request_query.left,
89133
event: announce_request_query.event,

src/torrust_http_tracker/handlers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub async fn handle_error(r: Rejection) -> std::result::Result<impl Reply, Infal
9696
/// Send announce response
9797
fn send_announce_response(announce_request: &AnnounceRequest, torrent_stats: TorrentStats, peers: Vec<TorrentPeer>, interval: u32) -> WebResult<impl Reply> {
9898
let http_peers: Vec<Peer> = peers.iter().map(|peer| Peer {
99-
peer_id: peer.peer_id.0.clone(),
99+
peer_id: peer.peer_id.to_string(),
100100
ip: peer.peer_addr.ip(),
101101
port: peer.peer_addr.port()
102102
}).collect();

src/torrust_http_tracker/request.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
use std::net::{IpAddr, SocketAddr};
22
use serde::{Deserialize};
3-
use crate::InfoHash;
3+
use crate::{InfoHash, PeerId};
44
use crate::torrust_http_tracker::Bytes;
55

66
#[derive(Deserialize)]
77
pub struct AnnounceRequestQuery {
88
pub downloaded: Bytes,
99
pub uploaded: Bytes,
1010
pub key: String,
11-
pub peer_id: String,
1211
pub port: u16,
1312
pub left: Bytes,
1413
pub event: Option<String>,
@@ -21,7 +20,7 @@ pub struct AnnounceRequest {
2120
pub forwarded_ip: Option<IpAddr>,
2221
pub downloaded: Bytes,
2322
pub uploaded: Bytes,
24-
pub peer_id: String,
23+
pub peer_id: PeerId,
2524
pub port: u16,
2625
pub left: Bytes,
2726
pub event: Option<String>,

src/tracker.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@ impl TorrentPeer {
5757
pub fn from_udp_announce_request(announce_request: &aquatic_udp_protocol::AnnounceRequest, remote_ip: IpAddr, host_opt_ip: Option<IpAddr>) -> Self {
5858
let peer_addr = TorrentPeer::peer_addr_from_ip_and_port_and_opt_host_ip(remote_ip, host_opt_ip, announce_request.port.0);
5959

60-
let peer_id = String::from_utf8_lossy(&announce_request.peer_id.0).parse().unwrap_or("unknown".to_string());
61-
6260
TorrentPeer {
63-
peer_id: PeerId(peer_id),
61+
peer_id: PeerId(announce_request.peer_id.0),
6462
peer_addr,
6563
updated: std::time::Instant::now(),
6664
uploaded: announce_request.bytes_uploaded,
@@ -73,9 +71,6 @@ impl TorrentPeer {
7371
pub fn from_http_announce_request(announce_request: &AnnounceRequest, remote_ip: IpAddr, host_opt_ip: Option<IpAddr>) -> Self {
7472
let peer_addr = TorrentPeer::peer_addr_from_ip_and_port_and_opt_host_ip(remote_ip, host_opt_ip, announce_request.port);
7573

76-
let max_string_size = announce_request.peer_id.len().clamp(0, 40);
77-
let peer_id = announce_request.peer_id[..max_string_size].to_string();
78-
7974
let event: AnnounceEvent = if let Some(event) = &announce_request.event {
8075
match event.as_ref() {
8176
"started" => AnnounceEvent::Started,
@@ -88,7 +83,7 @@ impl TorrentPeer {
8883
};
8984

9085
TorrentPeer {
91-
peer_id: PeerId(peer_id),
86+
peer_id: announce_request.peer_id.clone(),
9287
peer_addr,
9388
updated: std::time::Instant::now(),
9489
uploaded: NumberOfBytes(announce_request.uploaded as i64),

0 commit comments

Comments
 (0)