mod datagram;
mod exec;
mod file;
mod parse;
mod pipe;
mod pty;
mod stdio;
mod stream;
mod sys;
mod tcp;
mod tty;
mod udp;
mod unix;
use std::num::NonZeroUsize;
use serde::{Deserialize, Serialize};
pub use self::{
datagram::Demux,
exec::{Exec, System},
file::File,
parse::ParseEndpointError,
pipe::Pipe,
pty::{Pty, PtyExec},
stdio::Stdio,
stream::{
BoxRead, BoxWrite, Connection, DatagramSocket, EndpointStream, ReadHalf, SyncHalves,
SyncRead, SyncWrite, WriteHalf,
},
sys::{PathGuard, size_if_pipe},
tcp::{Tcp, TcpListen},
tty::Tty,
udp::{Udp, UdpListen},
unix::{
Unix, UnixListen,
dgram::{UnixDgram, UnixDgramListen},
seqpacket::{UnixSeqpacket, UnixSeqpacketListen},
},
};
const DEFAULT_HOST: &str = "127.0.0.1";
const DEFAULT_PORT: u16 = 8000;
const DEFAULT_MAX_CONNECTIONS: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Endpoint {
Raw(String),
Spec(EndpointSpec),
}
impl Endpoint {
pub fn into_spec(self) -> Result<EndpointSpec, ParseEndpointError> {
match self {
Endpoint::Raw(raw) => raw.parse(),
Endpoint::Spec(spec) => Ok(spec),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
Source,
Sink,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum EndpointSpec {
#[serde(
alias = "TCP",
alias = "tcp-connect",
alias = "TCP-CONNECT",
alias = "connect",
alias = "CONNECT",
alias = "tcpconnect",
alias = "TCPCONNECT"
)]
Tcp(Tcp),
#[serde(
alias = "TCP-LISTEN",
alias = "tcplisten",
alias = "TCPLISTEN",
alias = "listen",
alias = "LISTEN"
)]
TcpListen(TcpListen),
Stdio(Stdio),
#[serde(
alias = "UNIX",
alias = "unix-connect",
alias = "UNIX-CONNECT",
alias = "uds",
alias = "UDS",
alias = "uds-connect",
alias = "UDS-CONNECT",
alias = "udsconnect",
alias = "UDSCONNECT"
)]
Unix(Unix),
#[serde(
alias = "UNIX-LISTEN",
alias = "unixlisten",
alias = "UNIXLISTEN",
alias = "uds-listen",
alias = "UDS-LISTEN",
alias = "udslisten",
alias = "UDSLISTEN"
)]
UnixListen(UnixListen),
#[serde(
alias = "UNIX-SEQPACKET",
alias = "unix-seqpkt",
alias = "UNIX-SEQPKT",
alias = "uds-seqpacket",
alias = "UDS-SEQPACKET",
alias = "seqpacket",
alias = "SEQPACKET"
)]
UnixSeqpacket(UnixSeqpacket),
#[serde(
alias = "UNIX-SEQPACKET-LISTEN",
alias = "unix-seqpkt-listen",
alias = "UNIX-SEQPKT-LISTEN",
alias = "uds-seqpacket-listen",
alias = "UDS-SEQPACKET-LISTEN",
alias = "seqpacket-listen",
alias = "SEQPACKET-LISTEN"
)]
UnixSeqpacketListen(UnixSeqpacketListen),
#[serde(
alias = "UNIX-DGRAM",
alias = "unix-datagram",
alias = "UNIX-DATAGRAM",
alias = "uds-dgram",
alias = "UDS-DGRAM"
)]
UnixDgram(UnixDgram),
#[serde(
alias = "UNIX-DGRAM-LISTEN",
alias = "unix-datagram-listen",
alias = "UNIX-DATAGRAM-LISTEN",
alias = "uds-dgram-listen",
alias = "UDS-DGRAM-LISTEN"
)]
UnixDgramListen(UnixDgramListen),
#[serde(alias = "fifo", alias = "FIFO", alias = "PIPE")]
Pipe(Pipe),
#[serde(alias = "open", alias = "FILE", alias = "OPEN")]
File(File),
Exec(Exec),
System(System),
#[serde(alias = "PTY")]
Pty(Pty),
#[serde(alias = "PTY-EXEC", alias = "ptyexec", alias = "PTYEXEC")]
PtyExec(PtyExec),
#[serde(alias = "TTY", alias = "serial", alias = "SERIAL")]
Tty(Tty),
#[serde(
alias = "UDP",
alias = "udp-connect",
alias = "UDP-CONNECT",
alias = "udpconnect",
alias = "UDPCONNECT"
)]
Udp(Udp),
#[serde(alias = "UDP-LISTEN", alias = "udplisten", alias = "UDPLISTEN")]
UdpListen(UdpListen),
}
impl EndpointSpec {
pub fn is_listen(&self) -> bool {
matches!(
self,
Self::TcpListen(_)
| Self::UnixListen(_)
| Self::UnixSeqpacketListen(_)
| Self::UnixDgramListen(_)
| Self::UdpListen(_)
)
}
pub fn is_datagram(&self) -> bool {
matches!(
self,
Self::Udp(_)
| Self::UdpListen(_)
| Self::UnixSeqpacket(_)
| Self::UnixSeqpacketListen(_)
| Self::UnixDgram(_)
| Self::UnixDgramListen(_)
)
}
pub fn is_fork(&self) -> bool {
match self {
Self::TcpListen(e) => e.fork,
Self::UnixListen(e) => e.fork,
Self::UnixSeqpacketListen(e) => e.fork,
Self::UnixDgramListen(e) => e.fork,
Self::UdpListen(e) => e.fork,
_ => false,
}
}
pub fn name(&self) -> String {
match self {
Self::Tcp(e) => e.label(),
Self::TcpListen(e) => e.label(),
Self::Stdio(e) => e.label(),
Self::Unix(e) => e.label(),
Self::UnixListen(e) => e.label(),
Self::UnixSeqpacket(e) => e.label(),
Self::UnixSeqpacketListen(e) => e.label(),
Self::UnixDgram(e) => e.label(),
Self::UnixDgramListen(e) => e.label(),
Self::Pipe(e) => e.label(),
Self::File(e) => e.label(),
Self::Exec(e) => e.label(),
Self::System(e) => e.label(),
Self::Pty(e) => e.label(),
Self::PtyExec(e) => e.label(),
Self::Tty(e) => e.label(),
Self::Udp(e) => e.label(),
Self::UdpListen(e) => e.label(),
}
}
pub fn max_connections(&self) -> NonZeroUsize {
match self {
Self::TcpListen(TcpListen {
max_connections, ..
})
| Self::UnixListen(UnixListen {
max_connections, ..
})
| Self::UnixSeqpacketListen(UnixSeqpacketListen {
max_connections, ..
})
| Self::UnixDgramListen(UnixDgramListen {
max_connections, ..
})
| Self::UdpListen(UdpListen {
max_connections, ..
}) => max_connections.unwrap_or(DEFAULT_MAX_CONNECTIONS),
_ => DEFAULT_MAX_CONNECTIONS,
}
}
pub fn is_blocking_backed(&self) -> bool {
matches!(self, Self::File(_) | Self::Stdio(_) | Self::Pipe(_))
}
pub fn connect_sync(&self, dir: Direction, buffer: usize) -> anyhow::Result<SyncHalves> {
match self {
Self::Stdio(e) => Ok(e.connect_sync(buffer)),
Self::Pipe(e) => e.connect_sync(dir),
Self::File(e) => e.connect_sync(dir),
other => anyhow::bail!("{} has no synchronous form", other.name()),
}
}
pub async fn connect(&self, dir: Direction, buffer: usize) -> anyhow::Result<Connection> {
match self {
Self::Tcp(e) => e.connect().await,
Self::TcpListen(e) => e.connect().await,
Self::Stdio(e) => e.connect(buffer),
Self::Unix(e) => e.connect().await,
Self::UnixListen(e) => e.connect().await,
Self::UnixSeqpacket(e) => e.connect().await,
Self::UnixSeqpacketListen(e) => e.connect().await,
Self::UnixDgram(e) => e.connect().await,
Self::UnixDgramListen(e) => e.connect(buffer).await,
Self::Pipe(e) => e.connect(dir).await,
Self::File(e) => e.connect(dir).await,
Self::Exec(e) => e.connect(buffer).await,
Self::System(e) => e.connect(buffer).await,
Self::Pty(e) => e.connect().await,
Self::PtyExec(e) => e.connect().await,
Self::Tty(e) => e.connect().await,
Self::Udp(e) => e.connect().await,
Self::UdpListen(e) => e.connect().await,
}
}
}