Skip to content

Commit 1b34d93

Browse files
committed
refactor: [torrust#654] UDP tracker client: use clap and anyhow
```console $ cargo run --bin udp_tracker_client announce 127.0.0.1:6969 9c38422213e30bff212b30c360d26f9a02136422 | jq Compiling torrust-tracker v3.0.0-alpha.12-develop (/home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker) Finished dev [optimized + debuginfo] target(s) in 2.60s Running `target/debug/udp_tracker_client '127.0.0.1:6969' 9c38422213e30bff212b30c360d26f9a02136422` { "announce_interval": 120, "leechers": 0, "peers": [], "seeders": 1, "transaction_id": -888840697 } ```
1 parent c526cc1 commit 1b34d93

File tree

1 file changed

+98
-43
lines changed

1 file changed

+98
-43
lines changed

src/bin/udp_tracker_client.rs

+98-43
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,132 @@
1-
use std::env;
1+
//! UDP Tracker client:
2+
//!
3+
//! Examples:
4+
//!
5+
//! Announce request:
6+
//!
7+
//! ```text
8+
//! cargo run --bin udp_tracker_client 127.0.0.1:6969 9c38422213e30bff212b30c360d26f9a02136422 | jq
9+
//! ```
10+
//!
11+
//! Announce response:
12+
//!
13+
//! ```json
14+
//! {
15+
//! "transaction_id": -888840697
16+
//! "announce_interval": 120,
17+
//! "leechers": 0,
18+
//! "seeders": 1,
19+
//! "peers": [
20+
//! "123.123.123.123:51289"
21+
//! ],
22+
//! }
23+
/// ````
224
use std::net::{Ipv4Addr, SocketAddr};
325
use std::str::FromStr;
426

27+
use anyhow::Context;
528
use aquatic_udp_protocol::common::InfoHash;
29+
use aquatic_udp_protocol::Response::{AnnounceIpv4, AnnounceIpv6};
630
use aquatic_udp_protocol::{
731
AnnounceEvent, AnnounceRequest, ConnectRequest, ConnectionId, NumberOfBytes, NumberOfPeers, PeerId, PeerKey, Port, Response,
832
TransactionId,
933
};
34+
use clap::{Parser, Subcommand};
1035
use log::{debug, LevelFilter};
36+
use serde_json::json;
1137
use torrust_tracker::shared::bit_torrent::info_hash::InfoHash as TorrustInfoHash;
1238
use torrust_tracker::shared::bit_torrent::tracker::udp::client::{UdpClient, UdpTrackerClient};
1339

1440
const ASSIGNED_BY_OS: i32 = 0;
1541
const RANDOM_TRANSACTION_ID: i32 = -888_840_697;
1642

43+
#[derive(Parser, Debug)]
44+
#[command(author, version, about, long_about = None)]
45+
struct Args {
46+
#[command(subcommand)]
47+
command: Command,
48+
}
49+
50+
#[derive(Subcommand, Debug)]
51+
enum Command {
52+
Announce {
53+
#[arg(value_parser = parse_socket_addr)]
54+
tracker_socket_addr: SocketAddr,
55+
#[arg(value_parser = parse_info_hash)]
56+
info_hash: TorrustInfoHash,
57+
},
58+
}
59+
1760
#[tokio::main]
18-
async fn main() {
61+
async fn main() -> anyhow::Result<()> {
1962
setup_logging(LevelFilter::Info);
2063

21-
let (remote_socket_addr, info_hash) = parse_arguments();
64+
let args = Args::parse();
2265

2366
// Configuration
2467
let local_port = ASSIGNED_BY_OS;
2568
let transaction_id = RANDOM_TRANSACTION_ID;
2669
let bind_to = format!("0.0.0.0:{local_port}");
2770

2871
// Bind to local port
29-
3072
debug!("Binding to: {bind_to}");
3173
let udp_client = UdpClient::bind(&bind_to).await;
3274
let bound_to = udp_client.socket.local_addr().unwrap();
3375
debug!("Bound to: {bound_to}");
3476

35-
// Connect to remote socket
77+
let response = match args.command {
78+
Command::Announce {
79+
tracker_socket_addr,
80+
info_hash,
81+
} => {
82+
debug!("Connecting to remote: udp://{tracker_socket_addr}");
3683

37-
debug!("Connecting to remote: udp://{remote_socket_addr}");
38-
udp_client.connect(&remote_socket_addr).await;
84+
udp_client.connect(&tracker_socket_addr.to_string()).await;
3985

40-
let udp_tracker_client = UdpTrackerClient { udp_client };
86+
let udp_tracker_client = UdpTrackerClient { udp_client };
4187

42-
let transaction_id = TransactionId(transaction_id);
88+
let transaction_id = TransactionId(transaction_id);
4389

44-
let connection_id = send_connection_request(transaction_id, &udp_tracker_client).await;
90+
let connection_id = send_connection_request(transaction_id, &udp_tracker_client).await;
4591

46-
let response = send_announce_request(
47-
connection_id,
48-
transaction_id,
49-
info_hash,
50-
Port(bound_to.port()),
51-
&udp_tracker_client,
52-
)
53-
.await;
92+
send_announce_request(
93+
connection_id,
94+
transaction_id,
95+
info_hash,
96+
Port(bound_to.port()),
97+
&udp_tracker_client,
98+
)
99+
.await
100+
}
101+
};
102+
103+
match response {
104+
AnnounceIpv4(announce) => {
105+
let json = json!({
106+
"transaction_id": announce.transaction_id.0,
107+
"announce_interval": announce.announce_interval.0,
108+
"leechers": announce.leechers.0,
109+
"seeders": announce.seeders.0,
110+
"peers": announce.peers.iter().map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)).collect::<Vec<_>>(),
111+
});
112+
let pretty_json = serde_json::to_string_pretty(&json).unwrap();
113+
println!("{pretty_json}");
114+
}
115+
AnnounceIpv6(announce) => {
116+
let json = json!({
117+
"transaction_id": announce.transaction_id.0,
118+
"announce_interval": announce.announce_interval.0,
119+
"leechers": announce.leechers.0,
120+
"seeders": announce.seeders.0,
121+
"peers6": announce.peers.iter().map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)).collect::<Vec<_>>(),
122+
});
123+
let pretty_json = serde_json::to_string_pretty(&json).unwrap();
124+
println!("{pretty_json}");
125+
}
126+
_ => println!("{response:#?}"),
127+
}
54128

55-
println!("{response:#?}");
129+
Ok(())
56130
}
57131

58132
fn setup_logging(level: LevelFilter) {
@@ -76,31 +150,12 @@ fn setup_logging(level: LevelFilter) {
76150
debug!("logging initialized.");
77151
}
78152

79-
fn parse_arguments() -> (String, TorrustInfoHash) {
80-
let args: Vec<String> = env::args().collect();
81-
82-
if args.len() != 3 {
83-
eprintln!("Error: invalid number of arguments!");
84-
eprintln!("Usage: cargo run --bin udp_tracker_client <UDP_TRACKER_SOCKET_ADDRESS> <INFO_HASH>");
85-
eprintln!("Example: cargo run --bin udp_tracker_client 144.126.245.19:6969 9c38422213e30bff212b30c360d26f9a02136422");
86-
std::process::exit(1);
87-
}
153+
fn parse_socket_addr(s: &str) -> anyhow::Result<SocketAddr> {
154+
s.parse().with_context(|| format!("failed to parse socket address: `{s}`"))
155+
}
88156

89-
let remote_socket_addr = &args[1];
90-
let _valid_socket_addr = remote_socket_addr.parse::<SocketAddr>().unwrap_or_else(|_| {
91-
panic!(
92-
"Invalid argument: `{}`. Argument 1 should be a valid socket address. For example: `144.126.245.19:6969`.",
93-
args[1]
94-
)
95-
});
96-
let info_hash = TorrustInfoHash::from_str(&args[2]).unwrap_or_else(|_| {
97-
panic!(
98-
"Invalid argument: `{}`. Argument 2 should be a valid infohash. For example: `9c38422213e30bff212b30c360d26f9a02136422`.",
99-
args[2]
100-
)
101-
});
102-
103-
(remote_socket_addr.to_string(), info_hash)
157+
fn parse_info_hash(s: &str) -> anyhow::Result<TorrustInfoHash> {
158+
TorrustInfoHash::from_str(s).map_err(|e| anyhow::Error::msg(format!("failed to parse info-hash `{s}`: {e:?}")))
104159
}
105160

106161
async fn send_connection_request(transaction_id: TransactionId, client: &UdpTrackerClient) -> ConnectionId {

0 commit comments

Comments
 (0)