296 lines
12 KiB
Rust
296 lines
12 KiB
Rust
use std::net::UdpSocket;
|
|
use std::time::Duration;
|
|
|
|
use std::process::{exit, Command};
|
|
use std::{env, thread};
|
|
use wireguard_proxy::{Args, ProxyClient, ProxyServer};
|
|
|
|
const PONG: [u8; 246] = [
|
|
0x6A, 0x2, 0x6B, 0xC, 0x6C, 0x3F, 0x6D, 0xC, 0xA2, 0xEA, 0xDA, 0xB6, 0xDC, 0xD6, 0x6E, 0x0,
|
|
0x22, 0xD4, 0x66, 0x3, 0x68, 0x2, 0x60, 0x60, 0xF0, 0x15, 0xF0, 0x7, 0x30, 0x0, 0x12, 0x1A,
|
|
0xC7, 0x17, 0x77, 0x8, 0x69, 0xFF, 0xA2, 0xF0, 0xD6, 0x71, 0xA2, 0xEA, 0xDA, 0xB6, 0xDC, 0xD6,
|
|
0x60, 0x1, 0xE0, 0xA1, 0x7B, 0xFE, 0x60, 0x4, 0xE0, 0xA1, 0x7B, 0x2, 0x60, 0x1F, 0x8B, 0x2,
|
|
0xDA, 0xB6, 0x60, 0xC, 0xE0, 0xA1, 0x7D, 0xFE, 0x60, 0xD, 0xE0, 0xA1, 0x7D, 0x2, 0x60, 0x1F,
|
|
0x8D, 0x2, 0xDC, 0xD6, 0xA2, 0xF0, 0xD6, 0x71, 0x86, 0x84, 0x87, 0x94, 0x60, 0x3F, 0x86, 0x2,
|
|
0x61, 0x1F, 0x87, 0x12, 0x46, 0x2, 0x12, 0x78, 0x46, 0x3F, 0x12, 0x82, 0x47, 0x1F, 0x69, 0xFF,
|
|
0x47, 0x0, 0x69, 0x1, 0xD6, 0x71, 0x12, 0x2A, 0x68, 0x2, 0x63, 0x1, 0x80, 0x70, 0x80, 0xB5,
|
|
0x12, 0x8A, 0x68, 0xFE, 0x63, 0xA, 0x80, 0x70, 0x80, 0xD5, 0x3F, 0x1, 0x12, 0xA2, 0x61, 0x2,
|
|
0x80, 0x15, 0x3F, 0x1, 0x12, 0xBA, 0x80, 0x15, 0x3F, 0x1, 0x12, 0xC8, 0x80, 0x15, 0x3F, 0x1,
|
|
0x12, 0xC2, 0x60, 0x20, 0xF0, 0x18, 0x22, 0xD4, 0x8E, 0x34, 0x22, 0xD4, 0x66, 0x3E, 0x33, 0x1,
|
|
0x66, 0x3, 0x68, 0xFE, 0x33, 0x1, 0x68, 0x2, 0x12, 0x16, 0x79, 0xFF, 0x49, 0xFE, 0x69, 0xFF,
|
|
0x12, 0xC8, 0x79, 0x1, 0x49, 0x2, 0x69, 0x1, 0x60, 0x4, 0xF0, 0x18, 0x76, 0x1, 0x46, 0x40,
|
|
0x76, 0xFE, 0x12, 0x6C, 0xA2, 0xF2, 0xFE, 0x33, 0xF2, 0x65, 0xF1, 0x29, 0x64, 0x14, 0x65, 0x0,
|
|
0xD4, 0x55, 0x74, 0x15, 0xF2, 0x29, 0xD4, 0x55, 0x0, 0xEE, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
];
|
|
|
|
struct Server {
|
|
udp_host: String,
|
|
udp_target: String,
|
|
socket_timeout: Option<Duration>,
|
|
}
|
|
|
|
impl Server {
|
|
fn new(udp_host: String, udp_target: String, secs: u64) -> Server {
|
|
Server {
|
|
udp_host,
|
|
udp_target,
|
|
socket_timeout: match secs {
|
|
0 => None,
|
|
x => Some(Duration::from_secs(x)),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn start(&self) -> std::io::Result<usize> {
|
|
let udp_socket = UdpSocket::bind(&self.udp_host)?;
|
|
udp_socket.set_read_timeout(self.socket_timeout)?;
|
|
|
|
let sent = udp_socket.send_to(&PONG, &self.udp_target)?;
|
|
assert_eq!(sent, PONG.len());
|
|
|
|
let mut buf = [0u8; 2048];
|
|
match udp_socket.recv_from(&mut buf) {
|
|
Ok((len, src_addr)) => {
|
|
println!("udp got len: {} from src_addr: {}", len, src_addr);
|
|
assert_eq!(len, PONG.len());
|
|
assert_eq!(&buf[..len], &PONG[..]);
|
|
|
|
// now reply back to src_addr to make sure other direction works
|
|
let sent = udp_socket.send_to(&PONG, &src_addr)?;
|
|
assert_eq!(sent, PONG.len());
|
|
}
|
|
Err(e) => {
|
|
panic!("recv function failed first round: {:?}", e);
|
|
}
|
|
}
|
|
|
|
// if we are here, we successfully sent the reply, did we get it back?
|
|
match udp_socket.recv(&mut buf) {
|
|
Ok(len) => {
|
|
println!("udp got len: {}", len);
|
|
assert_eq!(len, PONG.len());
|
|
assert_eq!(&buf[..len], &PONG[..]);
|
|
}
|
|
Err(e) => {
|
|
panic!("recv function failed second round: {:?}", e);
|
|
}
|
|
}
|
|
|
|
println!("success! received back exactly what we sent both ways!");
|
|
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let raw_args = env::args().collect();
|
|
let args = Args::new(&raw_args);
|
|
let default_udp_host_target = "127.0.0.1:51820";
|
|
let default_socket_timeout = 10;
|
|
|
|
let tls_key = args.get_option(&["-tk", "--tls-key"]);
|
|
let tls_cert = args.get_option(&["-tc", "--tls-cert"]);
|
|
let pinnedpubkey = args.get_option(&["--pinnedpubkey"]);
|
|
|
|
let tls = if tls_key.is_some() && tls_cert.is_some() {
|
|
true
|
|
} else if tls_key.is_none() && tls_cert.is_none() {
|
|
false
|
|
} else {
|
|
println!("Error: if one of --tls-key or --tls-cert is specified both must be!");
|
|
exit(1);
|
|
};
|
|
|
|
let mut first_arg = args.get_str(&["-uh", "--udp-host"], default_udp_host_target);
|
|
if args.flag("-h") || args.flag("--help") {
|
|
println!(r#"usage: udp-test [options...]
|
|
-h, --help print this usage text
|
|
-s, --self-test run a self test through proxy
|
|
-is, --internal-self-test run a self test through proxy without
|
|
spawning other processes
|
|
-uh, --udp-host <ip:port> UDP host to listen on, default: {}
|
|
-ut, --udp-target <ip:port> UDP target to send packets to, default: {}
|
|
-st, --socket-timeout <seconds> Socket timeout (time to wait for data)
|
|
before terminating, default: {}
|
|
|
|
TLS option for self tests only, otherwise self tests are plaintext only:
|
|
-tk, --tls-key <ip:port> TLS key to listen with,
|
|
requires --tls-cert also
|
|
-tc, --tls-cert <ip:port> TLS cert to listen with,
|
|
requires --tls-key also
|
|
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
|
|
format is any number of base64 encoded
|
|
sha256 hashes preceded by "sha256//"
|
|
and separated by ";". Identical to curl's
|
|
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
|
|
Note: with both --tls-key and --tls-cert,
|
|
only for -is (not -s) - means stdin,
|
|
also the same file can work for both if you combine them into
|
|
one pem file
|
|
|
|
Environment variable support:
|
|
For every command line option, short and long, if you replace all
|
|
leading - with WGP_, and replace all remaining - with _, and uppercase
|
|
the whole thing, if you don't specify that command line option we will
|
|
read that environment variable for the argument. boolean arguments are
|
|
true if anything but unset, empty, 0, or false.
|
|
Examples:
|
|
--tcp-target ARG is WGP_TCP_TARGET=ARG
|
|
--socket-timeout 5 is WGP_SOCKET_TIMEOUT=5
|
|
--tls is WGP_TLS=1 or WGP_TLS=true
|
|
WGP_TLS=0 or WGP_TLS=false would be like not sending --tls
|
|
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
|
|
return;
|
|
} else if args.flag("-s") || args.flag("--self-test") {
|
|
// here is the hard work, we need to spawn proxyd and proxy from the same dir as udp-test...
|
|
let host = "127.0.0.1:51822";
|
|
let tcp_host = "127.0.0.1:5555";
|
|
let sleep = Duration::from_secs(5);
|
|
|
|
let udp_test = std::env::current_exe().expect("cannot get path to current executable");
|
|
let proxy = udp_test.clone().with_file_name("wireguard-proxy")
|
|
.with_extension(udp_test.extension().unwrap_or_else(|| "".as_ref()));
|
|
|
|
let udp_test = udp_test.to_str().expect("non-utf8 executable path?");
|
|
let proxy = proxy.to_str().expect("non-utf8 executable path?");
|
|
|
|
let mut proxyd_args = vec!["-th", tcp_host, "-ut", host];
|
|
|
|
let tls_key = tls_key.as_ref().map(String::as_str);
|
|
let tls_cert = tls_cert.as_ref().map(String::as_str);
|
|
if tls {
|
|
let tls_key = tls_key.unwrap();
|
|
let tls_cert = tls_cert.unwrap();
|
|
proxyd_args.extend(["-tk", &tls_key, "-tc", &tls_cert].iter().cloned());
|
|
}
|
|
|
|
println!("executing: {} {}", proxy, proxyd_args.join(" "));
|
|
let mut proxyd = Command::new(proxy.clone())
|
|
.args(&proxyd_args)
|
|
.spawn()
|
|
.expect("wireguard-proxy server failed to launch");
|
|
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
|
|
thread::sleep(sleep);
|
|
|
|
let mut proxy_args = vec!["-tt", tcp_host];
|
|
|
|
let pinnedpubkey = pinnedpubkey.as_ref().map(String::as_str);
|
|
if tls {
|
|
proxy_args.push("--tls");
|
|
if pinnedpubkey.is_some() {
|
|
proxy_args.push("--pinnedpubkey");
|
|
proxy_args.push(&pinnedpubkey.unwrap());
|
|
}
|
|
}
|
|
|
|
println!("executing: {} {}", proxy, proxy_args.join(" "));
|
|
let mut proxy = Command::new(proxy)
|
|
.args(proxy_args)
|
|
.spawn()
|
|
.expect("wireguard-proxy TLS client failed to launch");
|
|
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
|
|
thread::sleep(sleep);
|
|
|
|
println!("executing: {} -uh '{}'", udp_test, host);
|
|
let mut udp_test = Command::new(udp_test)
|
|
.arg("-uh")
|
|
.arg(host)
|
|
.spawn()
|
|
.expect("udp-test failed to launch");
|
|
println!("waiting: {:?} for udp-test to come up.....", sleep);
|
|
thread::sleep(sleep);
|
|
|
|
// ignore all these, what could we do anyway?
|
|
proxy.kill().ok();
|
|
proxyd.kill().ok();
|
|
udp_test.kill().ok();
|
|
|
|
exit(
|
|
udp_test
|
|
.wait()
|
|
.expect("could not get udp-test exit code")
|
|
.code()
|
|
.expect("could not get udp-test exit code"),
|
|
);
|
|
} else if args.flag("-is") || args.flag("--internal-self-test") {
|
|
let host = "127.0.0.1:51822";
|
|
let tcp_host = "127.0.0.1:5555";
|
|
let sleep = Duration::from_secs(5);
|
|
|
|
let proxy_server = ProxyServer::new(
|
|
tcp_host.to_owned(),
|
|
host.to_owned(),
|
|
"127.0.0.1".to_owned(),
|
|
30000,
|
|
30100,
|
|
0,
|
|
);
|
|
|
|
println!(
|
|
"udp_target: {}, udp_bind_host_range: 127.0.0.1:30000-30100, socket_timeout: {:?}",
|
|
proxy_server.client_handler.udp_target, proxy_server.client_handler.socket_timeout,
|
|
);
|
|
|
|
if tls {
|
|
let tls_key = tls_key.unwrap().to_owned();
|
|
let tls_cert = tls_cert.unwrap().to_owned();
|
|
println!("executing: wireguard-proxy -th '{}' -ut '{}' -tk '{}' -tc '{}'", tcp_host, host, tls_key, tls_cert);
|
|
thread::spawn(move || proxy_server.start_tls(&tls_key, &tls_cert).expect("error running TLS proxy_server"));
|
|
} else {
|
|
println!("executing: wireguard-proxy -th '{}' -ut '{}'", tcp_host, host);
|
|
thread::spawn(move || proxy_server.start().expect("error running proxy_server"));
|
|
}
|
|
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
|
|
thread::sleep(sleep);
|
|
|
|
let proxy_client = ProxyClient::new(
|
|
"127.0.0.1:51820".to_owned(),
|
|
tcp_host.to_owned(),
|
|
15,
|
|
);
|
|
|
|
println!(
|
|
"udp_host: {}, tcp_target: {}, socket_timeout: {:?}",
|
|
proxy_client.udp_host,
|
|
proxy_client.tcp_target,
|
|
proxy_client.socket_timeout,
|
|
);
|
|
|
|
if tls {
|
|
let hostname = tcp_host.split(":").next();
|
|
let pinnedpubkey = pinnedpubkey.as_ref().map(String::as_str);
|
|
match pinnedpubkey {
|
|
Some(pinnedpubkey) =>
|
|
println!("executing: wireguard-proxy -tt {} --tls --pinnedpubkey {}", tcp_host, pinnedpubkey),
|
|
None =>
|
|
println!("executing: wireguard-proxy -tt {} --tls", tcp_host),
|
|
}
|
|
// this is a little funky, is this the only way to do it?
|
|
let pinnedpubkey = pinnedpubkey.map(&str::to_owned);
|
|
// can use pinnedpubkey.as_deref() below when it's stabilized
|
|
thread::spawn(move || proxy_client.start_tls(hostname, pinnedpubkey.as_ref().map(String::as_str)).expect("error running proxy_client"));
|
|
} else {
|
|
println!("executing: wireguard-proxy -tt {}", tcp_host);
|
|
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
|
|
}
|
|
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
|
|
thread::sleep(sleep);
|
|
|
|
first_arg = host.to_owned();
|
|
}
|
|
|
|
let server = Server::new(
|
|
first_arg.to_owned(),
|
|
args.get_str(&["-ut", "--udp-target"], default_udp_host_target).to_owned(),
|
|
args.get(&["-st", "--socket-timeout"], default_socket_timeout),
|
|
);
|
|
|
|
println!(
|
|
"udp_host: {}, udp_target: {}, socket_timeout: {:?}",
|
|
server.udp_host, server.udp_target, server.socket_timeout,
|
|
);
|
|
|
|
server.start().expect("error running server");
|
|
}
|