Merge proxy/proxyd into single binary, make command line parsing more robust

This commit is contained in:
Travis Burtrum 2019-12-15 18:44:56 -05:00
parent 152bb991d4
commit 5e394bca1e
9 changed files with 176 additions and 111 deletions

View File

@ -2,15 +2,16 @@
Proxy wireguard UDP packets over TCP/TLS
`wireguard-proxyd` is a server-side daemon to accept TCP connections from multiple clients and pipe data to and from the specified UDP port
`wireguard-proxy` is a client-side daemon that accepts UDP packets on a local port from a single client, connects to a single remote TCP port, and pipes data between them
`wireguard-proxy` has 2 modes:
- server-side daemon to accept TCP connections from multiple clients and pipe data to and from the specified UDP port
- client-side daemon that accepts UDP packets on a local port from a single client, connects to a single remote TCP port, and pipes data between them
Testing:
`udp-test` is a utility to send a UDP packet and then receive a UDP packet and ensure they are the same, this verifies packets sent through proxy/proxyd are unmolested
`udp-test -s` runs udp-test against itself through proxyd/proxy by spawning actual binaries
`udp-test -is` runs udp-test against itself through proxyd/proxy in same executable by using library, so does not test command line parsing etc
`test.sh` runs udp-test against itself, then the udp-test self tests above, and then through proxyd/proxy in the shell script
- `udp-test` is a utility to send a UDP packet and then receive a UDP packet and ensure they are the same, this verifies packets sent through proxy server/client are unmolested
- `udp-test -s` runs udp-test against itself through proxy server/client by spawning actual binaries
- `udp-test -is` runs udp-test against itself through proxy server/client in same executable by using library, so does not test command line parsing etc
- `test.sh` runs udp-test against itself, the udp-test self tests above, and through proxy server/client in the shell script
Testing with GNU netcat:

View File

@ -53,7 +53,6 @@ test_script:
before_deploy:
# TODO Update this to build the artifacts that matter to you
- cargo rustc --target %TARGET% --release --bin wireguard-proxy -- -C lto
- cargo rustc --target %TARGET% --release --bin wireguard-proxyd -- -C lto
- ps: ci\before_deploy.ps1
deploy:

View File

@ -11,7 +11,6 @@ Set-Location $STAGE
$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip"
# TODO Update this to package the right artifacts
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\wireguard-proxyd.exe" '.\'
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\wireguard-proxy.exe" '.\'
7z a "$ZIP" *

View File

@ -19,17 +19,16 @@ main() {
# TODO Update this to build the artifacts that matter to you
cross rustc --bin wireguard-proxy --target $TARGET --release -- -C lto
cross rustc --bin wireguard-proxyd --target $TARGET --release -- -C lto
# TODO Update this to package the right artifacts, this needs to handle .exe too...
case $TARGET in
x86_64-pc-windows-gnu)
strip target/$TARGET/release/wireguard-proxy.exe target/$TARGET/release/wireguard-proxyd.exe || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy.exe target/$TARGET/release/wireguard-proxyd.exe $stage/
strip target/$TARGET/release/wireguard-proxy.exe || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy.exe $stage/
;;
*)
strip target/$TARGET/release/wireguard-proxy target/$TARGET/release/wireguard-proxyd || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy target/$TARGET/release/wireguard-proxyd $stage/
strip target/$TARGET/release/wireguard-proxy || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy $stage/
;;
esac

View File

@ -70,41 +70,53 @@ impl Server {
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
let mut first_arg = args.get_str(1, "127.0.0.1:51820");
if first_arg.contains("-h") {
println!(
"usage: {} [-h] [-s run a self test through proxy/proxyd] [-is run a self test through proxy/proxyd without spawning other processes] [udp_host, 127.0.0.1:51820] [udp_target, 127.0.0.1:51820] [socket_timeout, 10]",
args.get_str(0, "udp-test")
);
let default_udp_host_target = "127.0.0.1:51820";
let default_socket_timeout = 10;
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: {}
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
return;
} else if first_arg.contains("-s") {
} 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 = args.get_str(0, "udp-test");
let proxyd = udp_test.clone().replace("udp-test", "wireguard-proxyd");
let udp_test = args.get_str_idx(0, "udp-test");
let proxy = udp_test.clone().replace("udp-test", "wireguard-proxy");
println!("executing: {} '{}' '{}'", proxyd, tcp_host, host);
let mut proxyd = Command::new(proxyd)
println!("executing: {} -th '{}' -ut '{}'", proxy, tcp_host, host);
let mut proxyd = Command::new(proxy.clone())
.arg("-th")
.arg(tcp_host)
.arg("-ut")
.arg(host)
.spawn()
.expect("wireguard-proxyd failed to launch");
println!("waiting: {:?} for wireguard-proxyd to come up.....", sleep);
.expect("wireguard-proxy server failed to launch");
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
thread::sleep(sleep);
println!("executing: {}", proxy);
println!("executing: {} -tt {}", proxy, tcp_host);
let mut proxy = Command::new(proxy)
.arg("-tt")
.arg(tcp_host)
.spawn()
.expect("wireguard-proxy failed to launch");
println!("waiting: {:?} for wireguard-proxy to come up.....", sleep);
.expect("wireguard-proxy client failed to launch");
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
thread::sleep(sleep);
println!("executing: {} '{}'", udp_test, host);
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");
@ -123,7 +135,7 @@ fn main() {
.code()
.expect("could not get udp-test exit code"),
);
} else if first_arg.contains("-is") {
} 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);
@ -142,9 +154,9 @@ fn main() {
proxy_server.client_handler.udp_target, proxy_server.client_handler.socket_timeout,
);
println!("executing: wireguard-proxyd '{}' '{}'", tcp_host, host);
println!("executing: wireguard-proxy -th '{}' -ut '{}'", tcp_host, host);
thread::spawn(move || proxy_server.start().expect("error running proxy_server"));
println!("waiting: {:?} for wireguard-proxyd to come up.....", sleep);
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
thread::sleep(sleep);
let proxy_client = ProxyClient::new(
@ -160,9 +172,9 @@ fn main() {
proxy_client.socket_timeout,
);
println!("executing: wireguard-proxy");
println!("executing: wireguard-proxy -tt {}", tcp_host);
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
println!("waiting: {:?} for wireguard-proxy to come up.....", sleep);
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
thread::sleep(sleep);
first_arg = host;
@ -170,8 +182,8 @@ fn main() {
let server = Server::new(
first_arg.to_owned(),
args.get_str(2, "127.0.0.1:51820").to_owned(),
args.get(3, 10),
args.get_str(&["-ut", "--udp-target"], default_udp_host_target).to_owned(),
args.get(&["-st", "--socket-timeout"], default_socket_timeout),
);
println!(

View File

@ -1,21 +1,62 @@
use std::env;
use wireguard_proxy::{Args, ProxyClient};
use wireguard_proxy::{Args, ProxyClient, ProxyServer};
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
if args.get_str(1, "").contains("-h") {
println!(
"usage: {} [-h] [udp_host, 127.0.0.1:51820] [tcp_target, 127.0.0.1:5555] [socket_timeout, 0]",
args.get_str(0, "wireguard-proxy")
);
let default_udp_host_target = "127.0.0.1:51820";
let default_socket_timeout = 0;
let tcp_target = args.get_option(&["-tt", "--tcp-target"]);
let tcp_host = args.get_option(&["-th", "--tcp-host"]);
if args.flag("-h") || args.flag("--help") ||
// one of them must be set
(tcp_target.is_none() && tcp_host.is_none()) ||
// but both cannot be set
(tcp_target.is_some() && tcp_host.is_some())
{
println!(r#"usage: wireguard-proxy [options...]
Client Mode (requires --tcp-target):
-tt, --tcp-target <ip:port> TCP target to send packets to, where
wireguard-proxy server is running
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
client here, default: {}
Server Mode (requires --tcp-host):
-th, --tcp-host <ip:port> TCP host to listen on
-ut, --udp-target <ip:port> UDP target to send packets to, where
wireguard server is running,
default: {}
-ur, --udp-bind-host-range <ip:low-high> UDP host and port range to bind to,
one port per TCP connection, to
listen on for UDP packets to send
back over the TCP connection,
default: 127.0.0.1:30000-40000
Common Options:
-h, --help print this usage text
-st, --socket-timeout <seconds> Socket timeout (time to wait for data)
before terminating, default: {}
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
return;
}
let socket_timeout = args.get(&["-st", "--socket-timeout"], default_socket_timeout);
if tcp_target.is_some() {
client(tcp_target.unwrap(), socket_timeout, args);
} else {
server(tcp_host.unwrap(), socket_timeout, args);
}
}
fn client(tcp_target: &str, socket_timeout: u64, args: Args) {
let proxy_client = ProxyClient::new(
args.get_str(1, "127.0.0.1:51820").to_owned(),
args.get_str(2, "127.0.0.1:5555").to_owned(),
args.get(3, 0),
args.get_str(&["-uh", "--udp-host"], "127.0.0.1:51820").to_owned(),
tcp_target.to_owned(),
socket_timeout,
);
println!(
@ -27,3 +68,45 @@ fn main() {
proxy_client.start().expect("error running proxy_client");
}
fn server(tcp_host: &str, socket_timeout: u64, args: Args) {
let udp_bind_host_range_str = args.get_str(&["-ur", "--udp-bind-host-range"], "127.0.0.1:30000-40000");
let mut udp_bind_host_range = udp_bind_host_range_str.split(":");
let udp_host = udp_bind_host_range
.next()
.expect("udp_bind_host_range host invalid");
let mut udp_ports = udp_bind_host_range
.next()
.expect("udp_bind_host_range port range invalid")
.split("-");
let udp_low_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let udp_high_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let proxy_server = ProxyServer::new(
tcp_host.to_owned(),
args.get_str(&["-ut", "--udp-target"], "127.0.0.1:51820").to_owned(),
udp_host.to_string(),
udp_low_port,
udp_high_port,
socket_timeout,
);
println!(
"udp_target: {}, udp_bind_host_range: {}, socket_timeout: {:?}",
proxy_server.client_handler.udp_target,
udp_bind_host_range_str,
proxy_server.client_handler.socket_timeout,
);
proxy_server.start().expect("error running proxy_server");
}

View File

@ -1,54 +0,0 @@
use std::env;
use wireguard_proxy::{Args, ProxyServer};
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
if args.get_str(1, "").contains("-h") {
println!(
"usage: {} [-h] [tcp_host, 127.0.0.1:5555] [udp_target, 127.0.0.1:51820] [udp_bind_host_range, 127.0.0.1:30000-40000] [socket_timeout, 0]",
args.get_str(0, "wireguard-proxyd")
);
return;
}
let udp_bind_host_range_str = args.get_str(3, "127.0.0.1:30000-40000");
let mut udp_bind_host_range = udp_bind_host_range_str.split(":");
let udp_host = udp_bind_host_range
.next()
.expect("udp_bind_host_range host invalid");
let mut udp_ports = udp_bind_host_range
.next()
.expect("udp_bind_host_range port range invalid")
.split("-");
let udp_low_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let udp_high_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let proxy_server = ProxyServer::new(
args.get_str(1, "127.0.0.1:5555").to_owned(),
args.get_str(2, "127.0.0.1:51820").to_owned(),
udp_host.to_string(),
udp_low_port,
udp_high_port,
args.get(4, 0),
);
println!(
"udp_target: {}, udp_bind_host_range: {}, socket_timeout: {:?}",
proxy_server.client_handler.udp_target,
udp_bind_host_range_str,
proxy_server.client_handler.socket_timeout,
);
proxy_server.start().expect("error running proxy_server");
}

View File

@ -13,14 +13,31 @@ impl<'a> Args<'a> {
pub fn new(args: &'a Vec<String>) -> Args {
Args { args }
}
pub fn get_str(&self, index: usize, def: &'a str) -> &'a str {
match self.args.get(index) {
pub fn flag(&self, flag: &'a str) -> bool {
self.args.contains(&flag.to_owned())
}
pub fn get_option(&self, flags: &[&'a str]) -> Option<&'a str> {
for flag in flags.iter() {
let mut found = false;
for arg in self.args.iter() {
if found {
return Some(arg);
}
if arg == flag {
found = true;
}
}
}
return None;
}
pub fn get_str(&self, flags: &[&'a str], def: &'a str) -> &'a str {
match self.get_option(flags) {
Some(ret) => ret,
None => def,
}
}
pub fn get<T: FromStr>(&self, index: usize, def: T) -> T {
match self.args.get(index) {
pub fn get<T: FromStr>(&self, flags: &[&'a str], def: T) -> T {
match self.get_option(flags) {
Some(ret) => match ret.parse::<T>() {
Ok(ret) => ret,
Err(_) => def, // or panic
@ -28,6 +45,12 @@ impl<'a> Args<'a> {
None => def,
}
}
pub fn get_str_idx(&self, index: usize, def: &'a str) -> &'a str {
match self.args.get(index) {
Some(ret) => ret,
None => def,
}
}
}
pub struct TcpUdpPipe {

17
test.sh
View File

@ -1,29 +1,32 @@
#!/bin/sh
set -x
# always run this clean
cargo clean
# first make sure udp-test succeeds running against itself
cargo run --release --bin udp-test || exit 1
# now run udp-test without spawning other processes
cargo run --release --bin udp-test -- -is || exit 1
# now run udp-test essentially just like the script below, but all in rust
cargo run --release --bin udp-test -- -s || exit 1
# now run proxyd pointing to udp-test
cargo run --release --bin wireguard-proxyd -- 127.0.0.1:5555 127.0.0.1:51822 &
cargo run --release --bin wireguard-proxy -- -th 127.0.0.1:5555 -ut 127.0.0.1:51822 &
proxyd_pid=$!
# wait for ports to be set up, this is fragile...
sleep 1
# proxy pointing to proxyd
cargo run --release --bin wireguard-proxy &
cargo run --release --bin wireguard-proxy -- -tt 127.0.0.1:5555 &
proxy_pid=$!
# wait for ports to be set up, this is fragile...
sleep 1
# and udp-test pointing to proxy, which then hops to proxyd, and finally back to udp-test
cargo run --release --bin udp-test -- 127.0.0.1:51822
cargo run --release --bin udp-test -- -uh 127.0.0.1:51822
udp_exit=$?
kill $proxyd_pid $proxy_pid
exit $udp_exit
[ $udp_exit -ne 0 ] && exit $udp_exit
# now run udp-test essentially just like the script above, but all in rust
cargo run --release --bin udp-test -- -s