From d80b238666d2874b90e53a4f664a516f55bf35b5 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Sun, 14 Jul 2019 01:37:05 -0400 Subject: [PATCH] Re-factor code, send packet length header in TCP, add test --- README.md | 10 +++- src/bin/udp-test.rs | 94 +++++++++++++++++++++++++++++++++++++ src/bin/wireguard-proxy.rs | 45 ++++-------------- src/bin/wireguard-proxyd.rs | 47 ++++--------------- src/lib.rs | 50 ++++++++++++++++++++ test.sh | 22 +++++++++ 6 files changed, 195 insertions(+), 73 deletions(-) create mode 100644 src/bin/udp-test.rs create mode 100755 test.sh diff --git a/README.md b/README.md index a48db7d..e78be01 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # wireguard-proxy -Server-side daemon to proxy multiple TCP connections to wireguard, client-side implementation coming here soon +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 + +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 +`test.sh` runs udp-test against itself and then through proxyd/proxy Testing with GNU netcat: diff --git a/src/bin/udp-test.rs b/src/bin/udp-test.rs new file mode 100644 index 0000000..c15fb35 --- /dev/null +++ b/src/bin/udp-test.rs @@ -0,0 +1,94 @@ +use std::net::UdpSocket; +use std::time::Duration; + +use std::env; +use wireguard_proxy::Args; + +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, +} + +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 { + let udp_host = UdpSocket::bind(&self.udp_host)?; + udp_host.set_read_timeout(self.socket_timeout)?; + + let udp_target = UdpSocket::bind("127.0.0.1:3401")?; + udp_target.connect(&self.udp_target)?; + + udp_target.send(&PONG)?; + + let mut buf = [0u8; 2048]; + match udp_host.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: {:?}", e); + } + } + + println!("success! received back exactly what we sent!"); + + Ok(0) + } +} + +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:51821] [udp_target, 127.0.0.1:51821] [socket_timeout, 1]", + args.get_str(0, "udp-test") + ); + return; + } + + let server = Server::new( + args.get_str(1, "127.0.0.1:51821").to_owned(), + args.get_str(2, "127.0.0.1:51821").to_owned(), + args.get(3, 1), + ); + + println!( + "udp_host: {}, udp_target: {}, socket_timeout: {:?}", + server.udp_host, server.udp_target, server.socket_timeout, + ); + + server.start().expect("error running server"); +} diff --git a/src/bin/wireguard-proxy.rs b/src/bin/wireguard-proxy.rs index e3157c5..ac9f5fc 100644 --- a/src/bin/wireguard-proxy.rs +++ b/src/bin/wireguard-proxy.rs @@ -1,10 +1,9 @@ -use std::io::{Read, Write}; use std::net::{TcpStream, UdpSocket}; use std::time::Duration; use std::env; use std::thread; -use wireguard_proxy::Args; +use wireguard_proxy::{Args, TcpUdpPipe}; struct Server { udp_host: String, @@ -27,49 +26,25 @@ impl Server { } fn start(&self) -> std::io::Result { - let mut tcp_stream = TcpStream::connect(&self.tcp_target)?; + let tcp_stream = TcpStream::connect(&self.tcp_target)?; tcp_stream.set_read_timeout(self.socket_timeout)?; let udp_socket = UdpSocket::bind(&self.udp_host)?; udp_socket.set_read_timeout(self.socket_timeout)?; - udp_socket.connect(&self.udp_target)?; + //udp_socket.connect(&self.udp_target)?; // this isn't strictly needed... just filters who we can receive from - let udp_socket_clone = udp_socket.try_clone().expect("clone udp_socket failed"); - let mut tcp_stream_clone = tcp_stream.try_clone().expect("clone tcp_stream failed"); - thread::spawn(move || { - let mut buf = [0u8; 2048]; - loop { - match udp_socket_clone.recv(&mut buf) { - Ok(len) => { - println!("udp got len: {}", len); - tcp_stream_clone - .write(&buf[..len]) - .expect("cannot write to tcp_clone"); - } - Err(e) => { - println!("recv function failed: {:?}", e); - break; - } - } - } + let mut udp_pipe = TcpUdpPipe::new(tcp_stream, udp_socket); + let mut udp_pipe_clone = udp_pipe.try_clone()?; + thread::spawn(move || loop { + udp_pipe_clone + .udp_to_tcp() + .expect("cannot write to tcp_clone"); }); - let mut buf = [0u8; 2048]; loop { - match tcp_stream.read(&mut buf) { - Ok(len) => { - println!("tcp got len: {}", len); - udp_socket.send(&buf[..len])?; - } - Err(e) => { - println!("Unable to read stream: {}", e); - break; - } - } + udp_pipe.tcp_to_udp()?; } - - Ok(0) } } diff --git a/src/bin/wireguard-proxyd.rs b/src/bin/wireguard-proxyd.rs index ee8d63a..4d3dd05 100644 --- a/src/bin/wireguard-proxyd.rs +++ b/src/bin/wireguard-proxyd.rs @@ -1,11 +1,10 @@ -use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream, UdpSocket}; use std::time::Duration; use std::env; use std::sync::Arc; use std::thread; -use wireguard_proxy::Args; +use wireguard_proxy::{Args, TcpUdpPipe}; struct Server { udp_target: String, @@ -35,7 +34,7 @@ impl Server { } } - fn handle_client(&self, mut tcp_stream: TcpStream) -> std::io::Result { + fn handle_client(&self, tcp_stream: TcpStream) -> std::io::Result { tcp_stream.set_read_timeout(self.socket_timeout)?; let mut port = self.udp_low_port; @@ -51,45 +50,19 @@ impl Server { } }; udp_socket.set_read_timeout(self.socket_timeout)?; - //udp_socket.connect(&self.udp_target)?; + udp_socket.connect(&self.udp_target)?; - let udp_socket_clone = udp_socket.try_clone().expect("clone udp_socket failed"); - let mut tcp_stream_clone = tcp_stream.try_clone().expect("clone tcp_stream failed"); - thread::spawn(move || { - let mut buf = [0u8; 2048]; - loop { - match udp_socket_clone.recv(&mut buf) { - Ok(len) => { - println!("udp got len: {}", len); - tcp_stream_clone - .write(&buf[..len]) - .expect("cannot write to tcp_clone"); - } - Err(e) => { - println!("recv function failed: {:?}", e); - break; - } - } - } + let mut udp_pipe = TcpUdpPipe::new(tcp_stream, udp_socket); + let mut udp_pipe_clone = udp_pipe.try_clone()?; + thread::spawn(move || loop { + udp_pipe_clone + .udp_to_tcp() + .expect("cannot write to tcp_clone"); }); - let mut buf = [0u8; 2048]; loop { - match tcp_stream.read(&mut buf) { - Ok(len) => { - println!("tcp got len: {}", len); - //udp_socket.send(&buf[..len])?; - let sent = udp_socket.send_to(&buf[..len], &self.udp_target)?; - println!("udp sent len: {}", sent); - } - Err(e) => { - println!("Unable to read stream: {}", e); - break; - } - } + udp_pipe.tcp_to_udp()?; } - - Ok(0) } } diff --git a/src/lib.rs b/src/lib.rs index 6293a42..09f0374 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +use std::io::{Read, Write}; +use std::net::{TcpStream, UdpSocket}; use std::str::FromStr; pub struct Args<'a> { @@ -24,3 +26,51 @@ impl<'a> Args<'a> { } } } + +pub struct TcpUdpPipe { + buf: [u8; 2050], // 2048 + 2 for len + tcp_stream: TcpStream, + udp_socket: UdpSocket, +} + +impl TcpUdpPipe { + pub fn new(tcp_stream: TcpStream, udp_socket: UdpSocket) -> TcpUdpPipe { + TcpUdpPipe { + tcp_stream, + udp_socket, + buf: [0u8; 2050], + } + } + + pub fn try_clone(&self) -> std::io::Result { + Ok(TcpUdpPipe::new( + self.tcp_stream.try_clone()?, + self.udp_socket.try_clone()?, + )) + } + + pub fn udp_to_tcp(&mut self) -> std::io::Result { + let len = self.udp_socket.recv(&mut self.buf[2..])?; + println!("udp got len: {}", len); + + self.buf[0] = ((len >> 8) & 0xFF) as u8; + self.buf[1] = (len & 0xFF) as u8; + + //let test_len = ((self.buf[0] as usize) << 8) + self.buf[1] as usize; + //println!("tcp sending test_len: {}", test_len); + + self.tcp_stream.write(&self.buf[..len + 2]) + } + + pub fn tcp_to_udp(&mut self) -> std::io::Result { + self.tcp_stream.read_exact(&mut self.buf[..2])?; + let len = ((self.buf[0] as usize) << 8) + self.buf[1] as usize; + println!("tcp expecting len: {}", len); + self.tcp_stream.read_exact(&mut self.buf[..len])?; + println!("tcp got len: {}", len); + self.udp_socket.send(&self.buf[..len]) + + //let sent = udp_socket.send_to(&buf[..len], &self.udp_target)?; + //assert_eq!(sent, len); + } +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..cfdcccb --- /dev/null +++ b/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# first make sure udp-test succeeds running against itself +cargo run --release --bin udp-test || 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 & +proxyd_pid=$! +# wait for ports to be set up, this is fragile... +sleep 1 +# proxy pointing to proxyd +cargo run --release --bin wireguard-proxy & +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 +udp_exit=$? + +kill $proxyd_pid $proxy_pid + +exit $udp_exit