├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── src └── main.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tcpproxy" 3 | version = "0.4.1" 4 | authors = ["Mahmoud Al-Qudsi "] 5 | description = "Cross-platform asynchronous multi-client TCP proxy; great tokio demo." 6 | homepage = "https://github.com/neosmart/tcpproxy" 7 | repository = "https://github.com/neosmart/tcpproxy" 8 | readme = "README.md" 9 | keywords = ["proxy", "tcp", "networking", "tokio"] 10 | categories = ["command-line-utilities", "network-programming"] 11 | license = "MIT" 12 | edition = "2018" 13 | 14 | [dependencies] 15 | futures = "0.3.31" 16 | getopts = "0.2.23" 17 | tokio = { version = "1.45.1", features = [ "io-util", "net", "rt-multi-thread", "macros", "sync" ] } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 NeoSmart Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CARGO = cargo 2 | VERSION ?= $$(cargo run -- --version | cut -d' ' -f2) 3 | 4 | .PHONY: all bench build check clean doc install publish run test update 5 | 6 | all: build 7 | 8 | bench: 9 | @$(CARGO) bench 10 | 11 | build: 12 | @env TERM=xterm-256color $(CARGO) build --color=always 2>&1 13 | 14 | check: build test 15 | 16 | clean: 17 | @$(CARGO) clean 18 | 19 | doc: 20 | @$(CARGO) doc 21 | 22 | install: 23 | @$(CARGO) install 24 | 25 | publish: 26 | @$(CARGO) publish 27 | 28 | run: build 29 | @$(CARGO) run 30 | 31 | test: 32 | @$(CARGO) test 33 | 34 | update: 35 | @$(CARGO) update 36 | 37 | ./target/x86_64-unknown-linux-musl/release/tcpproxy: Makefile src/main.rs 38 | env RUSTFLAGS="-Ctarget-feature=+crt-static" $(CARGO) build --release --target x86_64-unknown-linux-musl 39 | 40 | ./target/x86_64-unknown-linux-gnu/release/tcpproxy: Makefile src/main.rs 41 | env RUSTFLAGS= $(CARGO) build --release --target x86_64-unknown-linux-gnu 42 | 43 | ./target/x86_64-pc-windows-msvc/release/tcpproxy.exe: Makefile src/main.rs 44 | cmd.exe /C "set RUSTFLAGS=-Ctarget-feature=+crt-static && cargo.exe build --release --target x86_64-pc-windows-msvc" 45 | 46 | ./target/i686-pc-windows-msvc/release/tcpproxy.exe: Makefile src/main.rs 47 | cmd.exe /C "set RUSTFLAGS=-Ctarget-feature=+crt-static && cargo.exe build --release --target i686-pc-windows-msvc" 48 | 49 | ./tcpproxy-$(VERSION)-x86_64-unknown-linux-musl.tar.gz: ./target/x86_64-unknown-linux-musl/release/tcpproxy 50 | tar -czf ./tcpproxy-$(VERSION)-x86_64-unknown-linux-musl.tar.gz -C ./target/x86_64-unknown-linux-musl/release tcpproxy 51 | 52 | ./tcpproxy-$(VERSION)-x86_64-unknown-linux-gnu.tar.gz: ./target/x86_64-unknown-linux-gnu/release/tcpproxy 53 | tar -czf ./tcpproxy-$(VERSION)-x86_64-unknown-linux-gnu.tar.gz -C ./target/x86_64-unknown-linux-gnu/release tcpproxy 54 | 55 | ./tcpproxy-$(VERSION)-x86_64-pc-windows-msvc.zip: ./target/x86_64-pc-windows-msvc/release/tcpproxy.exe 56 | zip -j ./tcpproxy-$(VERSION)-x86_64-pc-windows-msvc.zip ./target/x86_64-pc-windows-msvc/release/tcpproxy.exe 57 | 58 | ./tcpproxy-$(VERSION)-i686-pc-windows-msvc.zip: ./target/i686-pc-windows-msvc/release/tcpproxy.exe 59 | zip -j ./tcpproxy-$(VERSION)-i686-pc-windows-msvc.zip ./target/x86_64-pc-windows-msvc/release/tcpproxy.exe 60 | 61 | release: 62 | $(MAKE) VERSION=$(VERSION) ./tcpproxy-$(VERSION)-x86_64-unknown-linux-musl.tar.gz 63 | $(MAKE) VERSION=$(VERSION) ./tcpproxy-$(VERSION)-x86_64-unknown-linux-gnu.tar.gz 64 | $(MAKE) VERSION=$(VERSION) ./tcpproxy-$(VERSION)-x86_64-pc-windows-msvc.zip 65 | $(MAKE) VERSION=$(VERSION) ./tcpproxy-$(VERSION)-i686-pc-windows-msvc.zip 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcpproxy 2 | _a simple, cross-platform, multi-client TCP proxy_ 3 | 4 | `tcpproxy` is a cross-platform, multi-client TCP proxy written in rust, that is designed for those "one-time" tasks where you usually end up spending more time installing a proxy server and setting up the myriad configuration files and options than you do actually using it. 5 | 6 | `tcpproxy` is completely asynchronous and built on top of the `tokio` async runtime. It was written to serve as an example of how bi-directional async networking code using rust futures and an async framework would look and is intentionally kept easy to understand. The code is updated regularly to take advantage of new tokio features and best practices (if/when they change). 7 | 8 | ## Usage 9 | 10 | `tcpproxy` is a command-line application. One instance of `tcpproxy` should be started for each remote endpoint you wish to proxy data to/from. All configuration is done via command-line arguments, in keeping with the spirit of this project. 11 | 12 | ``` 13 | tcpproxy REMOTE_HOST:PORT [-b BIND_ADDR] [-l LOCAL_PORT] 14 | 15 | Options: 16 | -b, --bind BIND_ADDR 17 | The address on which to listen for incoming requests, 18 | defaulting to localhost 19 | -l, --local-port LOCAL_PORT 20 | The local port to which tcpproxy should bind to, 21 | randomly chosen otherwise 22 | -d, --debug Enable debug mode w/ connection logging 23 | -h, --help Print usage info and exit 24 | -V, --version Print version info and exit 25 | ``` 26 | 27 | Where possible, sane defaults for arguments are provided automatically. 28 | 29 | ## Installation 30 | 31 | `tcpproxy` is available via `cargo`, the rust package manager. Installation is as follows: 32 | 33 | cargo install tcpproxy 34 | 35 | Pre-complied binaries for select platforms may be available from the `tcpproxy` homepage at https://neosmart.net/tcpproxy/ 36 | 37 | ## Project Status 38 | 39 | Depending on which language ecosystem you are coming from, this project may appear to be "unmaintained." Do not be fooled by a lack of updates for some length of time - this project is regularly updated **when needed** to fix bugs, improve code quality, use more modern rust coding patterns and conventions, and update dependencies. This project is *not*, however, updated for the sake of updating and is currently, in the humble opinion of its author, fairly feature-complete. The intention was always to provide a minimalistic (but still useful!) tcp proxy that can be quickly fired-up from the command line and put to good use. It is not intended to become comprehensive of any and all peripheral features and attempts to bundle "everything and the kitchen sink" will be respectfully but firmly declined. 40 | 41 | ## Contributing 42 | 43 | Pull requests are welcome, but for any major undertakings, please do open an issue first to make sure we're all on the same page! 44 | 45 | ## License and Authorship 46 | 47 | `tcpproxy` is developed and maintained by Mahmoud Al-Qudsi of NeoSmart Technologies. `tcpproxy` is open source and licensed under the terms of the MIT public license, made available to the general public without warranty in the hopes that it may prove both edifying and useful. 48 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use futures::FutureExt; 2 | use getopts::Options; 3 | use std::env; 4 | use std::sync::atomic::{AtomicBool, Ordering}; 5 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 6 | use tokio::net::{TcpListener, TcpStream}; 7 | use tokio::sync::broadcast; 8 | 9 | type BoxedError = Box; 10 | static DEBUG: AtomicBool = AtomicBool::new(false); 11 | const BUF_SIZE: usize = 1024; 12 | 13 | fn print_usage(out: &mut dyn std::io::Write, program: &str, opts: Options) { 14 | let program_path = std::path::PathBuf::from(program); 15 | let program_name = program_path.file_stem().unwrap().to_string_lossy(); 16 | let brief = format!( 17 | "Usage: {} REMOTE_HOST:PORT [-b BIND_ADDR] [-l LOCAL_PORT]", 18 | program_name 19 | ); 20 | _ = write!(out, "{}", opts.usage(&brief)); 21 | } 22 | 23 | #[tokio::main] 24 | async fn main() -> Result<(), BoxedError> { 25 | let args: Vec = env::args().collect(); 26 | let program = args[0].clone(); 27 | 28 | let mut opts = Options::new(); 29 | opts.optopt( 30 | "b", 31 | "bind", 32 | "The address on which to listen for incoming requests, defaulting to localhost", 33 | "BIND_ADDR", 34 | ); 35 | opts.optopt( 36 | "l", 37 | "local-port", 38 | "The local port to which tcpproxy should bind to, randomly chosen otherwise", 39 | "LOCAL_PORT", 40 | ); 41 | opts.optflag("d", "debug", "Enable debug mode w/ connection logging"); 42 | opts.optflag("h", "help", "Print usage info and exit"); 43 | opts.optflag("V", "version", "Print version info and exit"); 44 | 45 | let matches = match opts.parse(&args[1..]) { 46 | Ok(opts) => opts, 47 | Err(e) => { 48 | eprintln!("{}", e); 49 | print_usage(&mut std::io::stderr().lock(), &program, opts); 50 | std::process::exit(-1); 51 | } 52 | }; 53 | 54 | if matches.opt_present("h") { 55 | print_usage(&mut std::io::stdout().lock(), &program, opts); 56 | std::process::exit(0); 57 | } 58 | 59 | if matches.opt_present("V") { 60 | println!( 61 | "tcpproxy {} - {}", 62 | env!("CARGO_PKG_VERSION"), 63 | env!("CARGO_PKG_REPOSITORY") 64 | ); 65 | std::process::exit(0); 66 | } 67 | 68 | let remote = match matches.free.len() { 69 | 1 => matches.free[0].clone(), 70 | _ => { 71 | print_usage(&mut std::io::stderr().lock(), &program, opts); 72 | std::process::exit(-1); 73 | } 74 | }; 75 | 76 | if !remote.contains(':') { 77 | eprintln!("A remote port is required (REMOTE_ADDR:PORT)"); 78 | std::process::exit(-1); 79 | } 80 | 81 | DEBUG.store(matches.opt_present("d"), Ordering::Relaxed); 82 | // let local_port: i32 = matches.opt_str("l").unwrap_or("0".to_string()).parse()?; 83 | let local_port: i32 = matches.opt_str("l").map(|s| s.parse()).unwrap_or(Ok(0))?; 84 | let bind_addr = match matches.opt_str("b") { 85 | Some(addr) => addr, 86 | None => "127.0.0.1".to_owned(), 87 | }; 88 | 89 | forward(&bind_addr, local_port, remote).await 90 | } 91 | 92 | async fn forward(bind_ip: &str, local_port: i32, remote: String) -> Result<(), BoxedError> { 93 | // Listen on the specified IP and port 94 | let bind_addr = if !bind_ip.starts_with('[') && bind_ip.contains(':') { 95 | // Correctly format for IPv6 usage 96 | format!("[{}]:{}", bind_ip, local_port) 97 | } else { 98 | format!("{}:{}", bind_ip, local_port) 99 | }; 100 | let bind_sock = bind_addr 101 | .parse::() 102 | .expect("Failed to parse bind address"); 103 | let listener = TcpListener::bind(&bind_sock).await?; 104 | println!("Listening on {}", listener.local_addr().unwrap()); 105 | 106 | // `remote` should be either the host name or ip address, with the port appended. 107 | // It doesn't get tested/validated until we get our first connection, though! 108 | 109 | // We leak `remote` instead of wrapping it in an Arc to share it with future tasks since 110 | // `remote` is going to live for the lifetime of the server in all cases. 111 | // (This reduces MESI/MOESI cache traffic between CPU cores.) 112 | let remote: &str = Box::leak(remote.into_boxed_str()); 113 | 114 | // Two instances of this function are spawned for each half of the connection: client-to-server, 115 | // server-to-client. We can't use tokio::io::copy() instead (no matter how convenient it might 116 | // be) because it doesn't give us a way to correlate the lifetimes of the two tcp read/write 117 | // loops: even after the client disconnects, tokio would keep the upstream connection to the 118 | // server alive until the connection's max client idle timeout is reached. 119 | async fn copy_with_abort( 120 | read: &mut R, 121 | write: &mut W, 122 | mut abort: broadcast::Receiver<()>, 123 | ) -> tokio::io::Result 124 | where 125 | R: tokio::io::AsyncRead + Unpin, 126 | W: tokio::io::AsyncWrite + Unpin, 127 | { 128 | let mut copied = 0; 129 | let mut buf = [0u8; BUF_SIZE]; 130 | loop { 131 | let bytes_read; 132 | tokio::select! { 133 | biased; 134 | 135 | result = read.read(&mut buf) => { 136 | use std::io::ErrorKind::{ConnectionReset, ConnectionAborted}; 137 | bytes_read = result.or_else(|e| match e.kind() { 138 | // Consider these to be part of the proxy life, not errors 139 | ConnectionReset | ConnectionAborted => Ok(0), 140 | _ => Err(e) 141 | })?; 142 | }, 143 | _ = abort.recv() => { 144 | break; 145 | } 146 | } 147 | 148 | if bytes_read == 0 { 149 | break; 150 | } 151 | 152 | // While we ignore some read errors above, any error writing data we've already read to 153 | // the other side is always treated as exceptional. 154 | write.write_all(&buf[0..bytes_read]).await?; 155 | copied += bytes_read; 156 | } 157 | 158 | Ok(copied) 159 | } 160 | 161 | loop { 162 | let (mut client, client_addr) = listener.accept().await?; 163 | 164 | tokio::spawn(async move { 165 | println!("New connection from {}", client_addr); 166 | 167 | // Establish connection to upstream for each incoming client connection 168 | let mut remote = match TcpStream::connect(remote).await { 169 | Ok(result) => result, 170 | Err(e) => { 171 | eprintln!("Error establishing upstream connection: {e}"); 172 | return; 173 | } 174 | }; 175 | let (mut client_read, mut client_write) = client.split(); 176 | let (mut remote_read, mut remote_write) = remote.split(); 177 | 178 | let (cancel, _) = broadcast::channel::<()>(1); 179 | let (remote_copied, client_copied) = tokio::join! { 180 | copy_with_abort(&mut remote_read, &mut client_write, cancel.subscribe()) 181 | .then(|r| { let _ = cancel.send(()); async { r } }), 182 | copy_with_abort(&mut client_read, &mut remote_write, cancel.subscribe()) 183 | .then(|r| { let _ = cancel.send(()); async { r } }), 184 | }; 185 | 186 | match client_copied { 187 | Ok(count) => { 188 | if DEBUG.load(Ordering::Relaxed) { 189 | eprintln!( 190 | "Transferred {} bytes from proxy client {} to upstream server", 191 | count, client_addr 192 | ); 193 | } 194 | } 195 | Err(err) => { 196 | eprintln!( 197 | "Error writing bytes from proxy client {} to upstream server", 198 | client_addr 199 | ); 200 | eprintln!("{}", err); 201 | } 202 | }; 203 | 204 | match remote_copied { 205 | Ok(count) => { 206 | if DEBUG.load(Ordering::Relaxed) { 207 | eprintln!( 208 | "Transferred {} bytes from upstream server to proxy client {}", 209 | count, client_addr 210 | ); 211 | } 212 | } 213 | Err(err) => { 214 | eprintln!( 215 | "Error writing from upstream server to proxy client {}!", 216 | client_addr 217 | ); 218 | eprintln!("{}", err); 219 | } 220 | }; 221 | 222 | () 223 | }); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.69" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 31 | dependencies = [ 32 | "addr2line", 33 | "cc", 34 | "cfg-if", 35 | "libc", 36 | "miniz_oxide", 37 | "object", 38 | "rustc-demangle", 39 | ] 40 | 41 | [[package]] 42 | name = "bytes" 43 | version = "1.10.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 46 | 47 | [[package]] 48 | name = "cc" 49 | version = "1.2.27" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" 52 | dependencies = [ 53 | "shlex", 54 | ] 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.1" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 61 | 62 | [[package]] 63 | name = "futures" 64 | version = "0.3.31" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 67 | dependencies = [ 68 | "futures-channel", 69 | "futures-core", 70 | "futures-executor", 71 | "futures-io", 72 | "futures-sink", 73 | "futures-task", 74 | "futures-util", 75 | ] 76 | 77 | [[package]] 78 | name = "futures-channel" 79 | version = "0.3.31" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 82 | dependencies = [ 83 | "futures-core", 84 | "futures-sink", 85 | ] 86 | 87 | [[package]] 88 | name = "futures-core" 89 | version = "0.3.31" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 92 | 93 | [[package]] 94 | name = "futures-executor" 95 | version = "0.3.31" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 98 | dependencies = [ 99 | "futures-core", 100 | "futures-task", 101 | "futures-util", 102 | ] 103 | 104 | [[package]] 105 | name = "futures-io" 106 | version = "0.3.31" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 109 | 110 | [[package]] 111 | name = "futures-macro" 112 | version = "0.3.31" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 115 | dependencies = [ 116 | "proc-macro2", 117 | "quote", 118 | "syn", 119 | ] 120 | 121 | [[package]] 122 | name = "futures-sink" 123 | version = "0.3.31" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 126 | 127 | [[package]] 128 | name = "futures-task" 129 | version = "0.3.31" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 132 | 133 | [[package]] 134 | name = "futures-util" 135 | version = "0.3.31" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 138 | dependencies = [ 139 | "futures-channel", 140 | "futures-core", 141 | "futures-io", 142 | "futures-macro", 143 | "futures-sink", 144 | "futures-task", 145 | "memchr", 146 | "pin-project-lite", 147 | "pin-utils", 148 | "slab", 149 | ] 150 | 151 | [[package]] 152 | name = "getopts" 153 | version = "0.2.23" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" 156 | dependencies = [ 157 | "unicode-width", 158 | ] 159 | 160 | [[package]] 161 | name = "gimli" 162 | version = "0.28.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 165 | 166 | [[package]] 167 | name = "libc" 168 | version = "0.2.173" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" 171 | 172 | [[package]] 173 | name = "memchr" 174 | version = "2.7.5" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 177 | 178 | [[package]] 179 | name = "miniz_oxide" 180 | version = "0.7.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 183 | dependencies = [ 184 | "adler", 185 | ] 186 | 187 | [[package]] 188 | name = "mio" 189 | version = "1.0.4" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 192 | dependencies = [ 193 | "libc", 194 | "wasi", 195 | "windows-sys 0.59.0", 196 | ] 197 | 198 | [[package]] 199 | name = "object" 200 | version = "0.32.2" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 203 | dependencies = [ 204 | "memchr", 205 | ] 206 | 207 | [[package]] 208 | name = "pin-project-lite" 209 | version = "0.2.16" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 212 | 213 | [[package]] 214 | name = "pin-utils" 215 | version = "0.1.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 218 | 219 | [[package]] 220 | name = "proc-macro2" 221 | version = "1.0.95" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 224 | dependencies = [ 225 | "unicode-ident", 226 | ] 227 | 228 | [[package]] 229 | name = "quote" 230 | version = "1.0.40" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 233 | dependencies = [ 234 | "proc-macro2", 235 | ] 236 | 237 | [[package]] 238 | name = "rustc-demangle" 239 | version = "0.1.23" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 242 | 243 | [[package]] 244 | name = "shlex" 245 | version = "1.3.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 248 | 249 | [[package]] 250 | name = "slab" 251 | version = "0.4.9" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 254 | dependencies = [ 255 | "autocfg", 256 | ] 257 | 258 | [[package]] 259 | name = "socket2" 260 | version = "0.5.10" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 263 | dependencies = [ 264 | "libc", 265 | "windows-sys 0.52.0", 266 | ] 267 | 268 | [[package]] 269 | name = "syn" 270 | version = "2.0.103" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" 273 | dependencies = [ 274 | "proc-macro2", 275 | "quote", 276 | "unicode-ident", 277 | ] 278 | 279 | [[package]] 280 | name = "tcpproxy" 281 | version = "0.4.1" 282 | dependencies = [ 283 | "futures", 284 | "getopts", 285 | "tokio", 286 | ] 287 | 288 | [[package]] 289 | name = "tokio" 290 | version = "1.45.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 293 | dependencies = [ 294 | "backtrace", 295 | "bytes", 296 | "libc", 297 | "mio", 298 | "pin-project-lite", 299 | "socket2", 300 | "tokio-macros", 301 | "windows-sys 0.52.0", 302 | ] 303 | 304 | [[package]] 305 | name = "tokio-macros" 306 | version = "2.5.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 309 | dependencies = [ 310 | "proc-macro2", 311 | "quote", 312 | "syn", 313 | ] 314 | 315 | [[package]] 316 | name = "unicode-ident" 317 | version = "1.0.18" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 320 | 321 | [[package]] 322 | name = "unicode-width" 323 | version = "0.2.1" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" 326 | 327 | [[package]] 328 | name = "wasi" 329 | version = "0.11.0+wasi-snapshot-preview1" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 332 | 333 | [[package]] 334 | name = "windows-sys" 335 | version = "0.52.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 338 | dependencies = [ 339 | "windows-targets", 340 | ] 341 | 342 | [[package]] 343 | name = "windows-sys" 344 | version = "0.59.0" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 347 | dependencies = [ 348 | "windows-targets", 349 | ] 350 | 351 | [[package]] 352 | name = "windows-targets" 353 | version = "0.52.6" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 356 | dependencies = [ 357 | "windows_aarch64_gnullvm", 358 | "windows_aarch64_msvc", 359 | "windows_i686_gnu", 360 | "windows_i686_gnullvm", 361 | "windows_i686_msvc", 362 | "windows_x86_64_gnu", 363 | "windows_x86_64_gnullvm", 364 | "windows_x86_64_msvc", 365 | ] 366 | 367 | [[package]] 368 | name = "windows_aarch64_gnullvm" 369 | version = "0.52.6" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 372 | 373 | [[package]] 374 | name = "windows_aarch64_msvc" 375 | version = "0.52.6" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 378 | 379 | [[package]] 380 | name = "windows_i686_gnu" 381 | version = "0.52.6" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 384 | 385 | [[package]] 386 | name = "windows_i686_gnullvm" 387 | version = "0.52.6" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 390 | 391 | [[package]] 392 | name = "windows_i686_msvc" 393 | version = "0.52.6" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 396 | 397 | [[package]] 398 | name = "windows_x86_64_gnu" 399 | version = "0.52.6" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 402 | 403 | [[package]] 404 | name = "windows_x86_64_gnullvm" 405 | version = "0.52.6" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 408 | 409 | [[package]] 410 | name = "windows_x86_64_msvc" 411 | version = "0.52.6" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 414 | --------------------------------------------------------------------------------