1use std::net::{Ipv4Addr, SocketAddrV4};
2
3use anyhow::Context;
4use serde::{Deserialize, Serialize};
5use serde_with::{serde_as, Bytes};
6
7pub const PORT: u16 = 6881;
8
9#[derive(Debug, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum EventType {
12 Started,
13 Stopped,
14 Completed,
15}
16
17#[derive(Debug, Serialize, Deserialize)]
18pub struct TrackerRequest<'a> {
19 #[serde(skip_serializing)]
21 info_hash: [u8; 20],
22 peer_id: &'a str,
24 port: u16,
26 uploaded: u64,
28 downloaded: u64,
30 left: u64,
32 compact: u8,
34 event: Option<EventType>,
36}
37
38impl<'a> TrackerRequest<'a> {
39 pub fn new(
40 peer_id: &'a str,
41 info_hash: [u8; 20],
42 download_state: &[Option<u64>; 3],
43 event: Option<EventType>,
44 ) -> Self {
45 TrackerRequest {
46 info_hash,
47 peer_id,
48 port: PORT,
49 uploaded: download_state[0].unwrap_or_default(),
50 downloaded: download_state[1].unwrap_or_default(),
51 left: download_state[2].unwrap_or_default(),
52 compact: 1,
53 event,
54 }
55 }
56
57 pub fn into_query_params(self) -> anyhow::Result<String> {
58 let info_hash = form_urlencoded::byte_serialize(&self.info_hash).collect::<String>();
59
60 let mut serialized = serde_urlencoded::to_string(self).context("failed to serialize the tracker request")?;
64
65 serialized.push_str("&info_hash=");
67 serialized.push_str(&info_hash);
68
69 Ok(serialized)
70 }
71}
72
73#[serde_as]
74#[derive(Debug, Serialize, Deserialize)]
75pub struct TrackerResponse {
76 #[serde(rename = "failure reason")]
78 failure_reason: Option<String>,
79 interval: Option<u64>,
81 #[serde_as(as = "Option<Bytes>")]
83 peers: Option<Vec<u8>>,
84}
85
86impl TrackerResponse {
87 pub fn get_peers(&self) -> Option<anyhow::Result<Vec<SocketAddrV4>>> {
89 self.peers.as_ref().map(|peers| {
90 let mut peer_ips: Vec<SocketAddrV4> = Vec::with_capacity(peers.len() / 6);
92 for chunk in peers.chunks(6) {
93 let ip = try_into!(&chunk[..4], [u8; 4])?;
94 let port = ((chunk[4] as u16) << 8) | chunk[5] as u16;
95
96 let ip_addr = Ipv4Addr::from(ip);
97
98 peer_ips.push(SocketAddrV4::new(ip_addr, port));
99 }
100 Ok(peer_ips)
101 })
102 }
103}