#![cfg(feature = "udp")]
use std::io::Write;
use std::net::UdpSocket;
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
#[test]
fn udp_proxy_round_trips_via_socat() {
let runner = escargot::CargoBuild::new()
.bin("innisfree")
.run()
.expect("building innisfree via escargot");
let bin = runner.path().to_path_buf();
let listen_port = pick_ephemeral_udp_port();
let upstream_port = pick_ephemeral_udp_port();
let server = UdpSocket::bind(("127.0.0.1", upstream_port)).expect("binding echo upstream");
server
.set_read_timeout(Some(Duration::from_secs(5)))
.expect("setting upstream timeout");
let server_thread = thread::spawn(move || {
let mut buf = vec![0u8; 1500];
let (n, src) = server.recv_from(&mut buf).expect("upstream recv");
server.send_to(&buf[..n], src).expect("upstream send reply");
});
let mut proxy = Command::new(&bin)
.args([
"proxy",
"--dest-ip",
"127.0.0.1",
"--ports",
&format!("{upstream_port}:{listen_port}/UDP"),
])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("spawning innisfree proxy");
thread::sleep(Duration::from_millis(200));
let mut socat = Command::new("socat")
.args(["-T", "1", "-", &format!("UDP:127.0.0.1:{listen_port}")])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawning socat; is it installed and on PATH?");
socat
.stdin
.as_mut()
.unwrap()
.write_all(b"hello-udp\n")
.expect("writing to socat stdin");
drop(socat.stdin.take());
let socat_out = socat.wait_with_output().expect("waiting for socat");
let _ = proxy.kill();
let _ = proxy.wait();
server_thread.join().expect("joining upstream thread");
let stdout = String::from_utf8_lossy(&socat_out.stdout);
let stderr = String::from_utf8_lossy(&socat_out.stderr);
assert!(
stdout.contains("hello-udp"),
"socat didn't receive the echoed payload.\n\
status: {}\nstdout: {stdout:?}\nstderr: {stderr:?}",
socat_out.status,
);
}
fn pick_ephemeral_udp_port() -> u16 {
let s = UdpSocket::bind("127.0.0.1:0").expect("ephemeral UDP bind");
s.local_addr().expect("local addr").port()
}