-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.rs
156 lines (138 loc) · 5.34 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
use std::{net::IpAddr, num::NonZeroU16};
use crab_nat::PortMappingOptions;
#[derive(clap::Parser)]
struct Cli {
/// Delete the port mapping instead of creating one.
#[arg(short, long, default_value_t = false)]
delete: bool,
/// The gateway address to use. If empty, will attempt to determine the default gateway.
#[arg(short, long, default_value = "")]
gateway: String,
/// The local address the gateway will expect to see our address as. If empty, will attempt to use the default local address.
#[arg(short, long, default_value = "")]
local_address: String,
/// The internal port to map into.
#[arg(short = 'p', long, default_value_t = 8080)]
internal_port: u16,
/// 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.
#[arg(short, long, default_value = "tcp")]
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 = if args.local_address.is_empty() {
netdev::interface::get_local_ipaddr().expect("Could not determine a local address")
} else {
args.local_address
.parse()
.expect("Invalid local address format")
};
tracing::info!("Using local address: {local_address:#}");
// Get the gateway address from the command line or guess the default.
let gateway = if args.gateway.is_empty() {
let gateway = netdev::get_default_gateway().expect("Could not determine a gateway");
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"),
)
})
}
} else {
args.gateway.parse().expect("Invalid gateway format")
};
tracing::info!("Using gateway address: {gateway:#}");
// 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::try_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:#}");
}
if args.delete {
// Attempt a port unmapping request.
if let Err(e) = crab_nat::natpmp::try_drop_mapping(
gateway,
protocol,
NonZeroU16::new(args.internal_port),
None,
)
.await
{
return tracing::error!("Failed to unmap port: {e:#}");
}
// Print the mapped port information.
tracing::info!("Success! Deleted previous port mapping");
} else {
// Attempt a port mapping request.
let mapping = match crab_nat::PortMapping::new(
gateway,
local_address,
protocol,
NonZeroU16::new(args.internal_port).expect("Invalid 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:?} using {mapping_type:?}");
// 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...");
}
}
}