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
|
use eyre::{eyre, Context};
use std::{
io::Write,
net::{IpAddr, Ipv4Addr, SocketAddr},
time::Duration,
};
use crate::cli::Opt;
pub fn send_tcp(opt: &Opt, destination: SocketAddr) -> eyre::Result<()> {
let bind_addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
let family = match &destination {
SocketAddr::V4(_) => socket2::Domain::IPV4,
SocketAddr::V6(_) => socket2::Domain::IPV6,
};
let sock = socket2::Socket::new(family, socket2::Type::STREAM, Some(socket2::Protocol::TCP))
.wrap_err(eyre!("Failed to create TCP socket"))?;
eprintln!("Leaking TCP packets to {destination}");
sock.bind(&socket2::SockAddr::from(bind_addr))
.wrap_err(eyre!("Failed to bind TCP socket to {bind_addr}"))?;
let timeout = Duration::from_secs(opt.leak_timeout);
sock.set_write_timeout(Some(timeout))?;
sock.set_read_timeout(Some(timeout))?;
sock.connect_timeout(&socket2::SockAddr::from(destination), timeout)
.wrap_err(eyre!("Failed to connect to {destination}"))?;
let mut stream = std::net::TcpStream::from(sock);
stream
.write_all(opt.payload.as_bytes())
.wrap_err(eyre!("Failed to send message to {destination}"))?;
Ok(())
}
pub fn send_udp(opt: &Opt, destination: SocketAddr) -> Result<(), eyre::Error> {
let bind_addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
eprintln!("Leaking UDP packets to {destination}");
let family = match &destination {
SocketAddr::V4(_) => socket2::Domain::IPV4,
SocketAddr::V6(_) => socket2::Domain::IPV6,
};
let sock = socket2::Socket::new(family, socket2::Type::DGRAM, Some(socket2::Protocol::UDP))
.wrap_err("Failed to create UDP socket")?;
sock.bind(&socket2::SockAddr::from(bind_addr))
.wrap_err(eyre!("Failed to bind UDP socket to {bind_addr}"))?;
let std_socket = std::net::UdpSocket::from(sock);
std_socket
.send_to(opt.payload.as_bytes(), destination)
.wrap_err(eyre!("Failed to send message to {destination}"))?;
Ok(())
}
#[cfg(target_os = "windows")]
pub fn send_ping(opt: &Opt, destination: IpAddr) -> eyre::Result<()> {
eprintln!("Leaking ICMP packets to {destination}");
ping::ping(
destination,
Some(Duration::from_secs(opt.leak_timeout)),
None,
None,
None,
None,
)?;
Ok(())
}
#[cfg(target_os = "macos")]
pub fn send_ping(opt: &Opt, destination: IpAddr) -> eyre::Result<()> {
eprintln!("Leaking ICMP packets to {destination}");
// On macOS, use dgramsock (SOCK_DGRAM) instead of the default sock type (SOCK_RAW),
// so that we don't need root privileges. Naturally, this does not work for Windows.
ping::dgramsock::ping(
destination,
Some(Duration::from_secs(opt.leak_timeout)),
None,
None,
None,
None,
)?;
Ok(())
}
// Older Linux distributions don't allow unprivileged users to send ICMP packets, even for
// SOCK_DGRAM sockets. We use the ping command (which has capabilities/setuid set) to get around
// that.
#[cfg(target_os = "linux")]
pub fn send_ping(opt: &Opt, destination: IpAddr) -> eyre::Result<()> {
eprintln!("Leaking ICMP packets to {destination}");
let mut cmd = std::process::Command::new("ping");
let timeout_sec = opt.leak_timeout.to_string();
cmd.args(["-c", "1", "-W", &timeout_sec, &destination.to_string()]);
let output = cmd.output().wrap_err(eyre!(
"Failed to execute ping for destination {destination}"
))?;
if !output.status.success() {
eprintln!(
"ping stdout:\n\n{}",
std::str::from_utf8(&output.stdout).unwrap_or("invalid utf8")
);
eprintln!(
"ping stderr:\n\n{}",
std::str::from_utf8(&output.stderr).unwrap_or("invalid utf8")
);
return Err(eyre!("ping for destination {destination} failed"));
}
Ok(())
}
|