Initital commit
This commit is contained in:
commit
4199fbde49
10 changed files with 581 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
110
Cargo.lock
generated
Normal file
110
Cargo.lock
generated
Normal 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
12
Cargo.toml
Normal 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
62
README.md
Normal 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
121
src/client.rs
Normal 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
19
src/lib.rs
Normal 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
23
src/main.rs
Normal 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
10
src/packet.rs
Normal 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
136
src/server.rs
Normal 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
87
src/socket_adapter.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue