autonatv2_client/
autonatv2_client.rs

1use std::{error::Error, net::Ipv4Addr, time::Duration};
2
3use clap::Parser;
4use libp2p::{
5    autonat,
6    futures::StreamExt,
7    identify, identity,
8    multiaddr::Protocol,
9    noise,
10    swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent},
11    tcp, yamux, Multiaddr, SwarmBuilder,
12};
13use rand::rngs::OsRng;
14use tracing_subscriber::EnvFilter;
15
16#[derive(Debug, Parser)]
17#[command(name = "libp2p autonatv2 client")]
18struct Opt {
19    /// Port where the client will listen for incoming connections.
20    #[arg(short = 'p', long, default_value_t = 0)]
21    listen_port: u16,
22
23    /// Address of the server where want to connect to.
24    #[arg(short = 'a', long)]
25    server_address: Multiaddr,
26
27    /// Probe interval in seconds.
28    #[arg(short = 't', long, default_value = "2")]
29    probe_interval: u64,
30}
31
32#[tokio::main]
33async fn main() -> Result<(), Box<dyn Error>> {
34    let _ = tracing_subscriber::fmt()
35        .with_env_filter(EnvFilter::from_default_env())
36        .try_init();
37
38    let opt = Opt::parse();
39
40    let mut swarm = SwarmBuilder::with_new_identity()
41        .with_tokio()
42        .with_tcp(
43            tcp::Config::default(),
44            noise::Config::new,
45            yamux::Config::default,
46        )?
47        .with_quic()
48        .with_dns()?
49        .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))?
50        .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10)))
51        .build();
52
53    swarm.listen_on(
54        Multiaddr::empty()
55            .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED))
56            .with(Protocol::Tcp(opt.listen_port)),
57    )?;
58
59    swarm.dial(
60        DialOpts::unknown_peer_id()
61            .address(opt.server_address)
62            .build(),
63    )?;
64
65    loop {
66        match swarm.select_next_some().await {
67            SwarmEvent::NewListenAddr { address, .. } => {
68                println!("Listening on {address:?}");
69            }
70            SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event {
71                server,
72                tested_addr,
73                bytes_sent,
74                result: Ok(()),
75            })) => {
76                println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified.");
77            }
78            SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event {
79                server,
80                tested_addr,
81                bytes_sent,
82                result: Err(e),
83            })) => {
84                println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}.");
85            }
86            SwarmEvent::ExternalAddrConfirmed { address } => {
87                println!("External address confirmed: {address}");
88            }
89            _ => {}
90        }
91    }
92}
93
94#[derive(NetworkBehaviour)]
95pub struct Behaviour {
96    autonat: autonat::v2::client::Behaviour,
97    identify: identify::Behaviour,
98}
99
100impl Behaviour {
101    pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self {
102        Self {
103            autonat: autonat::v2::client::Behaviour::new(
104                OsRng,
105                autonat::v2::client::Config::default()
106                    .with_probe_interval(Duration::from_secs(probe_interval)),
107            ),
108            identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)),
109        }
110    }
111}