-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.rs
166 lines (146 loc) · 5.83 KB
/
client.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use std::{net::IpAddr, num::NonZeroU16};
use crab_nat::PortMappingOptions;
/// A simple command line utility to manage NAT-PMP and PCP port mappings.
#[derive(clap::Parser)]
struct Cli {
/// Delete the port mapping on exit.
#[arg(short, long)]
delete: bool,
/// Delete all port mappings for the protocol through NAT-PMP. No mappings are created.
#[arg(long)]
delete_all: bool,
/// The gateway address to use. If empty, will attempt to determine a default gateway.
#[arg(short, long)]
gateway: Option<String>,
/// The local address the gateway will expect to see our address as. If empty, will attempt to determine a default network address.
#[arg(short, long)]
local_address: Option<String>,
/// The internal port to map into.
#[arg(short = 'p', long, default_value_t = NonZeroU16::new(8080).unwrap())]
internal_port: NonZeroU16,
/// The external port to try to map. Server is not guaranteed to use this port.
#[arg(short = 'e', long)]
external_port: Option<NonZeroU16>,
/// Fetch the external IP address through NAT-PMP and exit.
#[arg(short = 'x', long)]
external_ip: bool,
/// The protocol to map. Either "tcp" or "udp".
#[arg(short, long, default_value = "udp")]
internet_protocol: String,
}
#[tokio::main]
async fn main() {
use clap::Parser as _;
let args = Cli::parse();
// Initialize the default logger. This is optional to use the crate and currently only shows UDP retry attempts.
tracing_subscriber::fmt::init();
// Get the protocol from the command line or use the default.
let protocol = match args.internet_protocol.to_lowercase().as_str().trim() {
"udp" => crab_nat::InternetProtocol::Udp,
"tcp" => crab_nat::InternetProtocol::Tcp,
_ => panic!("Invalid protocol"),
};
let local_address = args.local_address.filter(|a| !a.is_empty()).map_or_else(
|| netdev::interface::get_local_ipaddr().expect("Could not determine a local address"),
|address| {
address
.parse()
.expect("Invalid local address, must be an IP address")
},
);
tracing::info!("Using local address: {local_address:#}");
// Get the gateway address from the command line or guess the default.
let gateway = args.gateway.filter(|g| !g.is_empty()).map_or_else(
|| {
// Attempt to get a sensible default gateway.
let gateway = netdev::get_default_gateway().expect("Could not determine a gateway");
// Attempt to get a gateway address matching the IP version of the local address.
if local_address.is_ipv4() {
gateway
.ipv4
.first()
.map(|ip| IpAddr::V4(*ip))
.unwrap_or_else(|| {
IpAddr::V6(
*gateway
.ipv6
.first()
.expect("No addresses found for default gateway"),
)
})
} else {
gateway
.ipv6
.first()
.map(|ip| IpAddr::V6(*ip))
.unwrap_or_else(|| {
IpAddr::V4(
*gateway
.ipv4
.first()
.expect("No addresses found for default gateway"),
)
})
}
},
|gateway| {
gateway
.parse()
.expect("Invalid gateway, must be an IP address")
},
);
tracing::info!("Using gateway address: {gateway:#}");
// If the delete all flag is set, attempt to delete all mappings for the protocol and exit.
if args.delete_all {
crab_nat::natpmp::try_drop_mapping(gateway, protocol, None, None)
.await
.unwrap_or_else(|e| {
tracing::error!("Failed to delete mappings: {e:#}");
});
tracing::info!("Successfully deleted all mappings for protocol {protocol:?}");
return;
}
// If the external IP flag is set, attempt to get the external IP and exit.
if args.external_ip {
let external_ip = match crab_nat::natpmp::external_address(gateway, None).await {
Ok(ip) => ip,
Err(e) => return tracing::error!("Failed to get external IP: {e:#}"),
};
return tracing::info!("External IP: {external_ip:#}");
}
// Attempt a port mapping request.
let mapping = match crab_nat::PortMapping::new(
gateway,
local_address,
protocol,
args.internal_port,
PortMappingOptions {
external_port: args.external_port,
..Default::default()
},
)
.await
{
Ok(m) => m,
Err(e) => return tracing::error!("Failed to map port: {e:#}"),
};
let protocol = mapping.protocol();
let external_port = mapping.external_port();
let internal_port = mapping.internal_port();
let lifetime = mapping.lifetime();
let mapping_type = mapping.mapping_type();
// Print the mapped port information.
tracing::info!("Successfully mapped protocol {protocol:?} on external port {external_port} to internal port {internal_port} with a lifetime of {lifetime:?} seconds using {mapping_type:?}");
if args.delete {
// Try to safely drop the mapping.
if let Err((e, m)) = mapping.try_drop().await {
tracing::error!(
"Failed to drop mapping {protocol:?} {gateway}:{}->{}: {e:?}",
m.external_port(),
m.internal_port()
);
} else {
tracing::info!("Successfully deleted the mapping...");
}
}
}