torrust_tracker/console/clients/checker/checks/
udp.rs1use 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")); 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 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 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 {
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 {
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}