feat: init commit
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,10 +3,6 @@
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
|
||||
266
Cargo.lock
generated
Normal file
266
Cargo.lock
generated
Normal file
@@ -0,0 +1,266 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand 0.4.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_util"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c152cad8a04f2de2840155adcc473dac011ffbe8ff33f8dfcfab489afa92d134"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"term",
|
||||
"term_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple-rust-udp-proxy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"rand 0.3.23",
|
||||
"rust_util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dirs",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "simple-rust-udp-proxy"
|
||||
version = "0.1.0"
|
||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
getopts = "0.2"
|
||||
rand = "0.3"
|
||||
rust_util = "0.6"
|
||||
@@ -1,3 +1,8 @@
|
||||
# simple-rust-udp-proxy
|
||||
|
||||
Simple Rust UDP Proxy
|
||||
Simple Rust UDP Proxy
|
||||
|
||||
|
||||
|
||||
from: https://github.com/neosmart/udpproxy
|
||||
|
||||
|
||||
190
src/main.rs
Normal file
190
src/main.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
extern crate getopts;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate rust_util;
|
||||
|
||||
use getopts::Options;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{ AtomicBool, Ordering };
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const TIMEOUT: u64 = 3 * 60 * 100; //3 minutes
|
||||
static mut DEBUG: bool = false;
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let program_path = std::path::PathBuf::from(program);
|
||||
let program_name = program_path.file_stem().unwrap().to_str().unwrap();
|
||||
let brief = format!("Usage: {} [-b BIND_ADDR] -l LOCAL_PORT -h REMOTE_ADDR -r REMOTE_PORT", program_name);
|
||||
information!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let program = args[0].clone();
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.reqopt("l",
|
||||
"local-port",
|
||||
"The local port to which udpproxy should bind to",
|
||||
"LOCAL_PORT");
|
||||
opts.reqopt("r",
|
||||
"remote-port",
|
||||
"The remote port to which UDP packets should be forwarded",
|
||||
"REMOTE_PORT");
|
||||
opts.reqopt("h",
|
||||
"host",
|
||||
"The remote address to which packets will be forwarded",
|
||||
"REMOTE_ADDR");
|
||||
opts.optopt("b",
|
||||
"bind",
|
||||
"The address on which to listen for incoming requests",
|
||||
"BIND_ADDR");
|
||||
opts.optflag("d", "debug", "Enable debug mode");
|
||||
|
||||
let matches = opts.parse(&args[1..])
|
||||
.unwrap_or_else(|_| {
|
||||
print_usage(&program, opts);
|
||||
std::process::exit(-1);
|
||||
});
|
||||
|
||||
unsafe {
|
||||
DEBUG = matches.opt_present("d");
|
||||
}
|
||||
let local_port: i32 = matches.opt_str("l").unwrap().parse().unwrap();
|
||||
let remote_port: i32 = matches.opt_str("r").unwrap().parse().unwrap();
|
||||
let remote_host = matches.opt_str("h").unwrap();
|
||||
let bind_addr = match matches.opt_str("b") {
|
||||
Some(addr) => addr,
|
||||
None => "127.0.0.1".to_owned(),
|
||||
};
|
||||
|
||||
forward(&bind_addr, local_port, &remote_host, remote_port);
|
||||
}
|
||||
|
||||
fn forward(bind_addr: &str, local_port: i32, remote_host: &str, remote_port: i32) {
|
||||
let local_addr = format!("{}:{}", bind_addr, local_port);
|
||||
let local = UdpSocket::bind(&local_addr).unwrap_or_else(|_| panic!("Unable to bind to {}", &local_addr));
|
||||
information!("Listening on {}", local.local_addr().unwrap());
|
||||
|
||||
let remote_addr = format!("{}:{}", remote_host, remote_port);
|
||||
|
||||
let responder = local
|
||||
.try_clone()
|
||||
.unwrap_or_else(|_| panic!("Failed to clone primary listening address socket {}", local.local_addr().unwrap()));
|
||||
let (main_sender, main_receiver) = channel::<(_, Vec<u8>)>();
|
||||
thread::spawn(move || {
|
||||
debugging!("Started new thread to deal out responses to clients");
|
||||
loop {
|
||||
let (dest, buf) = main_receiver.recv().unwrap();
|
||||
let to_send = buf.as_slice();
|
||||
responder
|
||||
.send_to(to_send, dest)
|
||||
.unwrap_or_else(|_| panic!("Failed to forward response from upstream server to client {}", dest));
|
||||
}
|
||||
});
|
||||
|
||||
let mut client_map = HashMap::new();
|
||||
let mut buf = [0; 64 * 1024];
|
||||
loop {
|
||||
let (num_bytes, src_addr) = local.recv_from(&mut buf).expect("Didn't receive data");
|
||||
|
||||
//we create a new thread for each unique client
|
||||
let mut remove_existing = false;
|
||||
loop {
|
||||
debugging!("Received packet from client {}", src_addr);
|
||||
|
||||
let mut ignore_failure = true;
|
||||
let client_id = format!("{}", src_addr);
|
||||
|
||||
if remove_existing {
|
||||
debugging!("Removing existing forwarder from map.");
|
||||
client_map.remove(&client_id);
|
||||
}
|
||||
|
||||
let sender = client_map.entry(client_id.clone()).or_insert_with(|| {
|
||||
//we are creating a new listener now, so a failure to send shoud be treated as an error
|
||||
ignore_failure = false;
|
||||
|
||||
let local_send_queue = main_sender.clone();
|
||||
let (sender, receiver) = channel::<Vec<u8>>();
|
||||
let remote_addr_copy = remote_addr.clone();
|
||||
thread::spawn(move|| {
|
||||
//regardless of which port we are listening to, we don't know which interface or IP
|
||||
//address the remote server is reachable via, so we bind the outgoing
|
||||
//connection to 0.0.0.0 in all cases.
|
||||
let temp_outgoing_addr = format!("0.0.0.0:{}", 1024 + rand::random::<u16>());
|
||||
debugging!("Establishing new forwarder for client {} on {}", src_addr, &temp_outgoing_addr);
|
||||
let upstream_send = UdpSocket::bind(&temp_outgoing_addr)
|
||||
.unwrap_or_else(|_| panic!("Failed to bind to transient address {}", &temp_outgoing_addr));
|
||||
let upstream_recv = upstream_send.try_clone()
|
||||
.unwrap_or_else(|_| panic!("Failed to clone client-specific connection to upstream!"));
|
||||
|
||||
let mut timeouts : u64 = 0;
|
||||
let timed_out = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let local_timed_out = timed_out.clone();
|
||||
thread::spawn(move|| {
|
||||
let mut from_upstream = [0; 64 * 1024];
|
||||
upstream_recv.set_read_timeout(Some(Duration::from_millis(TIMEOUT + 100))).unwrap();
|
||||
loop {
|
||||
match upstream_recv.recv_from(&mut from_upstream) {
|
||||
Ok((bytes_rcvd, _)) => {
|
||||
let to_send = from_upstream[..bytes_rcvd].to_vec();
|
||||
local_send_queue.send((src_addr, to_send))
|
||||
.expect("Failed to queue response from upstream server for forwarding!");
|
||||
},
|
||||
Err(_) => {
|
||||
if local_timed_out.load(Ordering::Relaxed) {
|
||||
debugging!("Terminating forwarder thread for client {} due to timeout", src_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
match receiver.recv_timeout(Duration::from_millis(TIMEOUT)) {
|
||||
Ok(from_client) => {
|
||||
upstream_send.send_to(from_client.as_slice(), &remote_addr_copy)
|
||||
.unwrap_or_else(|_| panic!("Failed to forward packet from client {} to upstream server!", src_addr));
|
||||
timeouts = 0; //reset timeout count
|
||||
},
|
||||
Err(_) => {
|
||||
timeouts += 1;
|
||||
if timeouts >= 10 {
|
||||
debugging!("Disconnecting forwarder for client {} due to timeout", src_addr);
|
||||
timed_out.store(true, Ordering::Relaxed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
sender
|
||||
});
|
||||
|
||||
let to_send = buf[..num_bytes].to_vec();
|
||||
match sender.send(to_send) {
|
||||
Ok(_) => {
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
if !ignore_failure {
|
||||
panic!("Failed to send message to datagram forwarder for client {}", client_id);
|
||||
}
|
||||
//client previously timed out
|
||||
debugging!("New connection received from previously timed-out client {}", client_id);
|
||||
remove_existing = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user