torrust_tracker/console/clients/checker/checks/
udp.rs

1use std::net::SocketAddr;
2use std::time::Duration;
3
4use aquatic_udp_protocol::TransactionId;
5use hex_literal::hex;
6use serde::Serialize;
7use url::Url;
8
9use crate::console::clients::udp::checker::Client;
10use crate::console::clients::udp::Error;
11
12#[derive(Debug, Clone, Serialize)]
13pub struct Checks {
14    remote_addr: SocketAddr,
15    results: Vec<(Check, Result<(), Error>)>,
16}
17
18#[derive(Debug, Clone, Serialize)]
19pub enum Check {
20    Setup,
21    Connect,
22    Announce,
23    Scrape,
24}
25
26#[allow(clippy::missing_panics_doc)]
27pub async fn run(udp_trackers: Vec<Url>, timeout: Duration) -> Vec<Result<Checks, Checks>> {
28    let mut results = Vec::default();
29
30    tracing::debug!("UDP trackers ...");
31
32    let info_hash = aquatic_udp_protocol::InfoHash(hex!("9c38422213e30bff212b30c360d26f9a02136422")); // # DevSkim: ignore DS173237
33
34    for remote_url in udp_trackers {
35        let remote_addr = resolve_socket_addr(&remote_url);
36
37        let mut checks = Checks {
38            remote_addr,
39            results: Vec::default(),
40        };
41
42        tracing::debug!("UDP tracker: {:?}", remote_url);
43
44        // Setup
45        let client = match Client::new(remote_addr, timeout).await {
46            Ok(client) => {
47                checks.results.push((Check::Setup, Ok(())));
48                client
49            }
50            Err(err) => {
51                checks.results.push((Check::Setup, Err(err)));
52                results.push(Err(checks));
53                break;
54            }
55        };
56
57        let transaction_id = TransactionId::new(1);
58
59        // Connect Remote
60        let connection_id = match client.send_connection_request(transaction_id).await {
61            Ok(connection_id) => {
62                checks.results.push((Check::Connect, Ok(())));
63                connection_id
64            }
65            Err(err) => {
66                checks.results.push((Check::Connect, Err(err)));
67                results.push(Err(checks));
68                break;
69            }
70        };
71
72        // Announce
73        {
74            let check = client
75                .send_announce_request(transaction_id, connection_id, info_hash.into())
76                .await
77                .map(|_| ());
78
79            checks.results.push((Check::Announce, check));
80        }
81
82        // Scrape
83        {
84            let check = client
85                .send_scrape_request(connection_id, transaction_id, &[info_hash.into()])
86                .await
87                .map(|_| ());
88
89            checks.results.push((Check::Scrape, check));
90        }
91
92        if checks.results.iter().any(|f| f.1.is_err()) {
93            results.push(Err(checks));
94        } else {
95            results.push(Ok(checks));
96        }
97    }
98
99    results
100}
101
102fn resolve_socket_addr(url: &Url) -> SocketAddr {
103    let socket_addr = url.socket_addrs(|| None).unwrap();
104    *socket_addr.first().unwrap()
105}
106
107#[cfg(test)]
108mod tests {
109    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
110
111    use url::Url;
112
113    use crate::console::clients::checker::checks::udp::resolve_socket_addr;
114
115    #[test]
116    fn it_should_resolve_the_socket_address_for_udp_scheme_urls_containing_a_domain() {
117        let socket_addr = resolve_socket_addr(&Url::parse("udp://localhost:8080").unwrap());
118
119        assert!(
120            socket_addr == SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080)
121                || socket_addr == SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080)
122        );
123    }
124
125    #[test]
126    fn it_should_resolve_the_socket_address_for_udp_scheme_urls_containing_an_ip() {
127        let socket_addr = resolve_socket_addr(&Url::parse("udp://localhost:8080").unwrap());
128
129        assert!(
130            socket_addr == SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080)
131                || socket_addr == SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080)
132        );
133    }
134}