upnp_example/
main.rs

1// Copyright 2023 Protocol Labs.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21#![doc = include_str!("../README.md")]
22
23use std::error::Error;
24
25use futures::prelude::*;
26use libp2p::{noise, swarm::SwarmEvent, upnp, yamux, Multiaddr};
27use tracing_subscriber::EnvFilter;
28
29#[tokio::main]
30async fn main() -> Result<(), Box<dyn Error>> {
31    let _ = tracing_subscriber::fmt()
32        .with_env_filter(EnvFilter::from_default_env())
33        .try_init();
34
35    let mut swarm = libp2p::SwarmBuilder::with_new_identity()
36        .with_tokio()
37        .with_tcp(
38            Default::default(),
39            noise::Config::new,
40            yamux::Config::default,
41        )?
42        .with_behaviour(|_| upnp::tokio::Behaviour::default())?
43        .build();
44
45    // Tell the swarm to listen on all interfaces and a random, OS-assigned
46    // port.
47    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
48
49    // Dial the peer identified by the multi-address given as the second
50    // command-line argument, if any.
51    if let Some(addr) = std::env::args().nth(1) {
52        let remote: Multiaddr = addr.parse()?;
53        swarm.dial(remote)?;
54        println!("Dialed {addr}")
55    }
56
57    loop {
58        match swarm.select_next_some().await {
59            SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
60            SwarmEvent::Behaviour(upnp::Event::NewExternalAddr(addr)) => {
61                println!("New external address: {addr}");
62            }
63            SwarmEvent::Behaviour(upnp::Event::GatewayNotFound) => {
64                println!("Gateway does not support UPnP");
65                break;
66            }
67            SwarmEvent::Behaviour(upnp::Event::NonRoutableGateway) => {
68                println!("Gateway is not exposed directly to the public Internet, i.e. it itself has a private IP address.");
69                break;
70            }
71            _ => {}
72        }
73    }
74    Ok(())
75}