Initital commit

This commit is contained in:
Daniella / Tove 2023-01-31 19:57:49 +01:00
commit 4199fbde49
10 changed files with 581 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

110
Cargo.lock generated Normal file
View file

@ -0,0 +1,110 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "enum-ordinalize"
version = "3.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a"
dependencies = [
"num-bigint",
"num-traits",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "proc-macro2"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "revpfw3"
version = "0.1.0"
dependencies = [
"enum-ordinalize",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "revpfw3"
repository = "https://github.com/tudbut/revpfw3"
desciption = "A tool to bypass portforwarding restrictions using some cheap VServer"
license = "MIT"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
enum-ordinalize = "3.1"

62
README.md Normal file
View file

@ -0,0 +1,62 @@
Reverse-PortForward V3
========================
This tool bypasses port restrictions of your router using some not-very-powerful
server (a cheap 1€ vserver will suffice.)
---
### How to download it
I will provide a windows and mac build shortly. Right now, I can only provide a linux
build.
All builds I make can be found in the
[releases](https://github.com/tudbut/revpfw3/releases/latest).
If my build doesn't work or your system doesn't have one, [install
Rust](https://rustup.rs) and run `cargo install revpfw3` or `cargo install --git
https://github.com/tudbut/revpfw3`.
---
### How to set it up:
1. Buy some cheap server online, it will only need
1. Enough disk space to run a 5MB program (I recommend about .5GB free after
OS is installed)
2. 500MB RAM or more
3. Flexible port settings
4. Not much CPU power
2. Download revpfw3 to it
3. Run it like this: `revpfw3 server <port> <key>` (I reocmmend doing it in a
loop)
4. Download it to your destination as well (your PC, a raspi, etc)
5. Run it like this: `revpfw3 client <ip of your bridge server> <port> localhost
<port to redirect (on local machine)> <key>`
6. To restart, end BOTH processes (remote and on your local server) and restart
them.
---
### Applications and special features:
- Minecraft servers tested and functional.
- HTTP tested and functional.
- Some third-party protocols tested and functional.
- This is not an HTTP-Proxy. It will work with any TCP protocol that isn't
reliant on TCPNODELAY.
- No disconnects, even when the sockets stay open for hours.
- Fast
- Little ping increase in normal applications
- A 1ms waiting delay before sending is built in to reduce stress and increase
efficiency by waiting for further data.
---
### As a rust library
Reverse-PortForward V3 supports being used as a library. `revpfw3::client` and
`revpfw3::server` are public, so you can use those. Keep in mind they will panic
when the connection to the corresponding client/server drops.

121
src/client.rs Normal file
View file

@ -0,0 +1,121 @@
use std::{
io::Read,
io::Write,
net::{Shutdown, TcpStream},
thread,
time::{Duration, SystemTime},
vec,
};
use crate::{io_sync, PacketType, SocketAdapter};
pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sleep_delay_ms: u64) {
let mut buf1 = [0u8; 1];
let mut buf4 = [0u8; 4];
let mut buf = [0; 1024];
let mut tcp = TcpStream::connect((ip, port)).unwrap();
println!("Syncing...");
tcp.write_all(&['R' as u8, 'P' as u8, 'F' as u8, 30])
.unwrap();
println!("Authenticating...");
tcp.write_all(&(key.len() as u32).to_be_bytes()).unwrap();
tcp.write_all(key.as_bytes()).unwrap();
println!("Syncing...");
tcp.read_exact(&mut buf4).unwrap();
if buf4 != ['R' as u8, 'P' as u8, 'F' as u8, 30] {
panic!("RPF30 header expected, but not found. Make sure the server is actually running revpfw3!");
}
tcp.write_all(&[PacketType::KeepAlive.ordinal() as u8])
.unwrap();
println!("READY!");
tcp.set_nonblocking(true).unwrap();
let mut tcp = SocketAdapter::new(tcp);
let mut sockets: Vec<SocketAdapter> = Vec::new();
let mut last_keep_alive = SystemTime::now();
loop {
let mut did_anything = false;
if last_keep_alive.elapsed().unwrap().as_secs() >= 60 {
panic!("connection dropped. exiting.");
}
let mut to_remove = vec![];
for (i, socket) in sockets.iter_mut().enumerate() {
if let Ok(x) = socket.poll(&mut buf) {
if let Some(len) = x {
if len == 0 {
to_remove.push(i);
} else {
tcp.write(&[PacketType::ServerData.ordinal() as u8])
.unwrap();
tcp.write(&(i as u32).to_be_bytes()).unwrap();
tcp.write(&(len as u32).to_be_bytes()).unwrap();
tcp.write(&buf[..len]).unwrap();
}
did_anything = true;
}
} else {
to_remove.push(i);
did_anything = true;
}
}
for i in to_remove.into_iter().rev() {
tcp.write(&[PacketType::CloseClient.ordinal() as u8])
.unwrap();
tcp.write(&(i as u32).to_be_bytes()).unwrap();
let _ = sockets.remove(i).internal.shutdown(Shutdown::Both);
}
tcp.update().unwrap();
if io_sync(tcp.internal.read_exact(&mut buf1))
.unwrap()
.is_none()
{
if !did_anything {
thread::sleep(Duration::from_millis(sleep_delay_ms));
}
continue;
}
let pt = PacketType::from_ordinal(buf1[0] as i8)
.expect("server/client version mismatch or broken TCP");
tcp.internal.set_nonblocking(false).unwrap();
match pt {
PacketType::NewClient => {
let tcp = TcpStream::connect((dest_ip, dest_port)).unwrap();
tcp.set_nonblocking(true).unwrap();
sockets.push(SocketAdapter::new(tcp));
}
PacketType::CloseClient => {
tcp.internal.read_exact(&mut buf4).unwrap();
let _ = sockets
.remove(u32::from_be_bytes(buf4) as usize)
.internal
.shutdown(Shutdown::Both);
}
PacketType::KeepAlive => {
last_keep_alive = SystemTime::now();
tcp.write(&[PacketType::KeepAlive.ordinal() as u8]).unwrap();
}
PacketType::ClientData => {
tcp.internal.read_exact(&mut buf4).unwrap();
let idx = u32::from_be_bytes(buf4) as usize;
tcp.internal.read_exact(&mut buf4).unwrap();
let len = u32::from_be_bytes(buf4) as usize;
tcp.internal.read_exact(&mut buf[..len]).unwrap();
let _ = sockets[idx].write_later(&buf[..len]);
}
PacketType::ServerData => unreachable!(),
}
tcp.internal.set_nonblocking(true).unwrap();
}
}

19
src/lib.rs Normal file
View file

@ -0,0 +1,19 @@
mod client;
mod packet;
mod server;
mod socket_adapter;
use std::io::{Error, ErrorKind};
pub use client::*;
pub(crate) use packet::*;
pub use server::*;
pub(crate) use socket_adapter::*;
pub(crate) fn io_sync<T>(result: Result<T, Error>) -> Result<Option<T>, Error> {
match result {
Ok(x) => Ok(Some(x)),
Err(x) if x.kind() == ErrorKind::WouldBlock => Ok(None),
Err(x) => Err(x),
}
}

23
src/main.rs Normal file
View file

@ -0,0 +1,23 @@
use std::env;
use revpfw3::{client, server};
fn main() {
let args: Vec<_> = env::args().skip(1).collect();
if (6..=7).contains(&args.len()) && args[0] == "client" {
client(
&args[1],
args[2].parse().unwrap(),
&args[3],
args[4].parse().unwrap(),
&args[5],
if args.len() == 7 { args[6].parse().unwrap() } else { 1 }
);
}
if (3..=4).contains(&args.len()) && args[0] == "server" {
server(args[1].parse().unwrap(), &args[2], if args.len() == 4 { args[3].parse().unwrap() } else { 1 });
}
eprintln!("Usage: \n\
\x20 revpfw3 server <port> <key> [<poll delay>]\n\
\x20 revpfw3 client <server ip> <server port> <destination ip> <destination port> <key> [<poll delay>]");
}

10
src/packet.rs Normal file
View file

@ -0,0 +1,10 @@
use enum_ordinalize::Ordinalize;
#[derive(Debug, PartialEq, Eq, Ordinalize)]
pub(crate) enum PacketType {
NewClient,
CloseClient,
KeepAlive,
ClientData,
ServerData,
}

136
src/server.rs Normal file
View file

@ -0,0 +1,136 @@
use std::{
io::Read,
io::Write,
net::{Shutdown, TcpListener},
thread,
time::{Duration, SystemTime},
vec,
};
use crate::{io_sync, PacketType, SocketAdapter};
pub fn server(port: u16, key: &str, sleep_delay_ms: u64) {
let mut buf1 = [0u8; 1];
let mut buf4 = [0u8; 4];
let mut buf = [0; 1024];
let tcpl = TcpListener::bind(("0.0.0.0", port)).unwrap();
let mut tcp = loop {
let Ok(mut tcp) = tcpl.accept() else { continue };
let Ok(()) = tcp.0.read_exact(&mut buf4) else {
tcp.0.shutdown(Shutdown::Both).unwrap();
continue;
};
if buf4 == ['R' as u8, 'P' as u8, 'F' as u8, 30] {
println!("Compatible client connected.");
if tcp.0.read_exact(&mut buf4).is_ok() && u32::from_be_bytes(buf4) == key.len() as u32 {
println!("Key length matches.");
let mut keybuf = vec![0u8; key.len()];
if tcp.0.read_exact(&mut keybuf).is_ok() && keybuf == key.as_bytes() {
println!("Accepted.");
break tcp.0;
}
println!("Key content does not match.");
}
println!("Key mismatch - forgetting client.");
}
};
tcp.write_all(&mut ['R' as u8, 'P' as u8, 'F' as u8, 30])
.unwrap();
tcp.set_nonblocking(true).unwrap();
tcpl.set_nonblocking(true).unwrap();
let mut tcp = SocketAdapter::new(tcp);
let mut sockets: Vec<SocketAdapter> = Vec::new();
let mut last_keep_alive_sent = SystemTime::now();
let mut last_keep_alive = SystemTime::now();
loop {
let mut did_anything = false;
if last_keep_alive_sent.elapsed().unwrap().as_secs() >= 20 {
last_keep_alive_sent = SystemTime::now();
tcp.write(&[PacketType::KeepAlive.ordinal() as u8]).unwrap();
}
if last_keep_alive.elapsed().unwrap().as_secs() >= 60 {
panic!("connection dropped. exiting.");
}
if let Ok(new) = tcpl.accept() {
new.0.set_nonblocking(true).unwrap();
sockets.push(SocketAdapter::new(new.0));
tcp.write(&[PacketType::NewClient.ordinal() as u8]).unwrap();
did_anything = true;
}
let mut to_remove = vec![];
for (i, socket) in sockets.iter_mut().enumerate() {
if let Ok(x) = socket.poll(&mut buf) {
if let Some(len) = x {
if len == 0 {
to_remove.push(i);
} else {
tcp.write(&[PacketType::ClientData.ordinal() as u8])
.unwrap();
tcp.write(&(i as u32).to_be_bytes()).unwrap();
tcp.write(&(len as u32).to_be_bytes()).unwrap();
tcp.write(&buf[..len]).unwrap();
}
did_anything = true;
}
} else {
to_remove.push(i);
did_anything = true;
}
}
for i in to_remove.into_iter().rev() {
tcp.write(&[PacketType::CloseClient.ordinal() as u8])
.unwrap();
tcp.write(&(i as u32).to_be_bytes()).unwrap();
let _ = sockets.remove(i).internal.shutdown(Shutdown::Both);
}
tcp.update().unwrap();
if io_sync(tcp.internal.read_exact(&mut buf1))
.unwrap()
.is_none()
{
if !did_anything {
thread::sleep(Duration::from_millis(sleep_delay_ms));
}
continue;
}
let pt = PacketType::from_ordinal(buf1[0] as i8)
.expect("server/client version mismatch or broken TCP");
tcp.internal.set_nonblocking(false).unwrap();
match pt {
PacketType::NewClient => unreachable!(),
PacketType::CloseClient => {
tcp.internal.read_exact(&mut buf4).unwrap();
let _ = sockets
.remove(u32::from_be_bytes(buf4) as usize)
.internal
.shutdown(Shutdown::Both);
}
PacketType::KeepAlive => {
last_keep_alive = SystemTime::now();
}
PacketType::ClientData => unreachable!(),
PacketType::ServerData => {
tcp.internal.read_exact(&mut buf4).unwrap();
let idx = u32::from_be_bytes(buf4) as usize;
tcp.internal.read_exact(&mut buf4).unwrap();
let len = u32::from_be_bytes(buf4) as usize;
tcp.internal.read_exact(&mut buf[..len]).unwrap();
let _ = sockets[idx].write_later(&buf[..len]);
}
}
tcp.internal.set_nonblocking(true).unwrap();
}
}

87
src/socket_adapter.rs Normal file
View file

@ -0,0 +1,87 @@
use std::{
io::{Error, Read},
io::{ErrorKind, Write},
net::TcpStream,
};
use crate::io_sync;
pub(crate) struct SocketAdapter {
pub(crate) internal: TcpStream,
written: usize,
to_write: usize,
write: [u8; 4096],
broken: Option<i32>,
}
impl SocketAdapter {
pub fn new(tcp: TcpStream) -> SocketAdapter {
Self {
internal: tcp,
written: 0,
to_write: 0,
write: [0u8; 4096],
broken: None,
}
}
pub fn write_later(&mut self, buf: &[u8]) -> Result<(), Error> {
if let Some(ref x) = self.broken {
return Err(Error::from_raw_os_error(*x));
}
let lidx = self.to_write + self.written + buf.len();
if lidx > self.write.len() && lidx - self.to_write < self.write.len() {
self.write
.copy_within(self.written..self.written + self.to_write, 0);
self.written = 0;
}
let Some(x) = self.write.get_mut(self.to_write + self.written..self.to_write + self.written + buf.len()) else {
self.broken = Some(Error::from(ErrorKind::TimedOut).raw_os_error().unwrap());
return Err(ErrorKind::TimedOut.into());
};
x.copy_from_slice(buf);
self.to_write += buf.len();
Ok(())
}
pub fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
self.write_later(buf)?;
if let Err(x) = self.update() {
self.broken = Some(x.raw_os_error().unwrap());
Err(x)
} else {
Ok(())
}
}
pub fn update(&mut self) -> Result<(), Error> {
if let Some(ref x) = self.broken {
return Err(Error::from_raw_os_error(*x));
}
if self.to_write == 0 {
return Ok(());
}
match self
.internal
.write(&self.write[self.written..self.written + self.to_write])
{
Ok(x) => {
self.to_write -= x;
self.written += x;
if self.to_write == 0 {
self.written = 0;
}
Ok(())
}
Err(x) => {
self.broken = Some(x.raw_os_error().unwrap());
Err(x)
}
}
}
pub fn poll(&mut self, buf: &mut [u8]) -> Result<Option<usize>, Error> {
self.update()?;
io_sync(self.internal.read(buf))
}
}