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…
Add table
Reference in a new issue