Replies: 2 comments 6 replies
-
More generally, how does socat do this? It is unclear to me. |
Beta Was this translation helpful? Give feedback.
4 replies
-
Here is TCP forwarder that passes the tests like socat (unrefactored): use std::net::{TcpListener,TcpStream,SocketAddr,Shutdown};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync + 'static>>;
fn forward(mut s1: TcpStream, mut s2: TcpStream) -> Result<()> {
use std::os::unix::io::AsRawFd;
// nix = "0.19.0"
use nix::fcntl::{fcntl,OFlag,FcntlArg};
use nix::poll::{poll, PollFd, PollFlags};
use std::io::{Read,Write,ErrorKind};
type UnwrittedDataInBuffer = std::ops::Range<usize>;
fcntl ( s1.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK) )?;
fcntl ( s2.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK) )?;
let mut buf_1to2 = [0u8; 1024];
let mut buf_2to1 = [0u8; 1024];
let mut datarange_1to2 : Option<UnwrittedDataInBuffer> = None;
let mut datarange_2to1 : Option<UnwrittedDataInBuffer> = None;
let mut finished_1to2 = false;
let mut finished_2to1 = false;
let mut polls : [PollFd; 2] = [PollFd::new(-1, PollFlags::empty()); 2];
let mut pollflags : [PollFlags; 2] = [PollFlags::empty(); 2];
//let mut active_polls : &mut [PollFd] ;
loop {
if finished_1to2 && finished_2to1 {
drop(s1);
drop(s2);
return Ok(());
}
pollflags[0] = PollFlags::POLLHUP;
pollflags[1] = PollFlags::POLLHUP;
if datarange_1to2.is_none() {
pollflags[0] |= PollFlags::POLLIN;
} else {
pollflags[1] |= PollFlags::POLLOUT;
}
if datarange_2to1.is_none() {
pollflags[1] |= PollFlags::POLLIN;
} else {
pollflags[0] |= PollFlags::POLLOUT;
}
polls[0] = PollFd::new(s1.as_raw_fd(), pollflags[0]);
polls[1] = PollFd::new(s2.as_raw_fd(), pollflags[1]);
poll(&mut polls[..], -1)?;
macro_rules! can_definitely_be_done_using_usual_function {
($pollidx_r:literal, $pollidx_w:literal, $datarange:ident, $buf:ident, $finished:ident, $socket_r:ident, $socket_w:ident,) => {
if let Some(r) = $datarange.take() {
if polls[$pollidx_w].revents().map(|x|x.contains(PollFlags::POLLOUT)) == Some(true) {
match $socket_w.write(& $buf[r.clone()]) {
Ok(x) => {
if x == r.len() {
$datarange = None;
} else {
$datarange = Some((r.start + x)..(r.end));
}
}
Err(e) if e.kind() == ErrorKind::WouldBlock => {
}
Err(e) => Err(e)?,
}
} else {
$datarange = Some(r);
}
} else {
if polls[$pollidx_w].revents().map(|x|x.contains(PollFlags::POLLHUP)) == Some(true) {
// Abort reading from socket when we know that
// writing to the other socket is destined to fail
$finished = true;
let _ = $socket_w.shutdown(Shutdown::Write);
// But for some strange reason absence of this section does not affect test results.
} else
if polls[$pollidx_r].revents().map(|x|x.contains(PollFlags::POLLIN)) == Some(true) {
match $socket_r.read(&mut $buf[..]) {
Ok(0) => {
$finished = true;
$socket_w.shutdown(Shutdown::Write)?;
}
Ok(x) => {
$datarange = Some(0..x);
}
Err(e) if e.kind() == ErrorKind::WouldBlock => {
}
Err(e) => Err(e)?,
};
}
}
}
}
can_definitely_be_done_using_usual_function!(
0,
1,
datarange_1to2,
buf_1to2,
finished_1to2,
s1,
s2,
);
can_definitely_be_done_using_usual_function!(
1,
0,
datarange_2to1,
buf_2to1,
finished_2to1,
s2,
s1,
);
}
}
fn main() -> Result<()> {
let ss = TcpListener::bind("127.0.0.1:5747".parse::<SocketAddr>().unwrap())?;
while let Ok((cs,_)) = ss.accept() {
std::thread::spawn(move || {
let g = TcpStream::connect("127.0.0.1:5746".parse::<SocketAddr>().unwrap()).unwrap();
if let Err(e) = forward(cs, g) {
eprintln!("{}", e);
}
});
}
Ok(())
} Also The naive forwarder above fails only the clogged-in-both-directions test. |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Naive attempt:
works reasonably, but fails to propagate socket close when the tunnel is clogged (unavailable for writing) in both directions.
I made a special checker for TCP tunnels: https://github.com/vi/tcptunnelchecker. It shows that
socat tcp-l:5747,fork,reuseaddr tcp:127.0.0.1:5746
is a properly implemented tunnel, but the one above is not:Beta Was this translation helpful? Give feedback.
All reactions