pub(super) mod socks;
mod tcp;
#[cfg(feature = "tproxy")]
mod tproxy;
mod udp;
use self::socks::{handle_socks, handle_socks_stdio};
use self::tcp::{handle_tcp, handle_tcp_stdio};
use self::udp::{handle_udp, handle_udp_stdio};
use crate::client::HandlerResources;
use crate::parse_remote::{LocalSpec, RemoteSpec};
use crate::parse_remote::{Protocol, Remote};
use std::io;
use thiserror::Error;
use tproxy::{handle_tproxy_tcp, handle_tproxy_udp};
use tracing::debug;
#[derive(Debug, Error)]
pub enum FatalError {
#[error(transparent)]
ClientIo(io::Error),
#[error("cannot request stream from the main loop")]
RequestStream,
#[error("cannot send datagram to the main loop")]
SendDatagram,
#[error("main loop exited without sending stream")]
MainLoopExitWithoutSendingStream,
#[error("transparent Proxy not enabled")]
TproxyNotEnabled,
}
#[tracing::instrument(skip_all, fields(remote = %remote), level = "debug")]
pub(super) async fn handle_remote(
remote: &'static Remote,
handler_resources: &'static HandlerResources,
) -> Result<(), FatalError> {
debug!("opening remote");
match (&remote.local_addr, &remote.remote_addr, remote.protocol) {
(LocalSpec::Inet((lhost, lport)), RemoteSpec::Inet((rhost, rport)), Protocol::Tcp) => {
handle_tcp(lhost, *lport, rhost, *rport, handler_resources).await
}
(LocalSpec::Inet((lhost, lport)), RemoteSpec::Inet((rhost, rport)), Protocol::Udp) => {
handle_udp(lhost, *lport, rhost, *rport, handler_resources).await
}
(LocalSpec::Stdio, RemoteSpec::Inet((rhost, rport)), Protocol::Tcp) => {
handle_tcp_stdio(rhost, *rport, handler_resources).await
}
(LocalSpec::Stdio, RemoteSpec::Inet((rhost, rport)), Protocol::Udp) => {
handle_udp_stdio(rhost, *rport, handler_resources).await
}
(LocalSpec::Inet((lhost, lport)), RemoteSpec::Socks, _) => {
handle_socks(lhost, *lport, handler_resources).await
}
(LocalSpec::Stdio, RemoteSpec::Socks, _) => {
handle_socks_stdio(handler_resources).await
}
(LocalSpec::Inet((lhost, lport)), RemoteSpec::Tproxy, Protocol::Tcp) => {
handle_tproxy_tcp(lhost, *lport, handler_resources).await
}
(LocalSpec::Inet((lhost, lport)), RemoteSpec::Tproxy, Protocol::Udp) => {
handle_tproxy_udp(lhost, *lport, handler_resources).await
}
(LocalSpec::Stdio, RemoteSpec::Tproxy, _) => {
unreachable!("`clap` should have rejected this combination (this is a bug)")
}
}
}
#[cfg(not(feature = "tproxy"))]
mod tproxy {
use super::{FatalError, HandlerResources};
pub(super) async fn handle_tproxy_tcp(
_lhost: &str,
_lport: u16,
_handler_resources: &HandlerResources,
) -> Result<(), FatalError> {
Err(FatalError::TproxyNotEnabled)
}
pub(super) async fn handle_tproxy_udp(
_lhost: &str,
_lport: u16,
_handler_resources: &HandlerResources,
) -> Result<(), FatalError> {
Err(FatalError::TproxyNotEnabled)
}
}