use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::{decode_handshake, encode_handshake, TunnelType, STATUS_OK};
#[derive(Debug)]
pub enum HandshakeError {
Io(std::io::Error),
Rejected,
NotFound,
}
impl std::fmt::Display for HandshakeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "I/O error: {e}"),
Self::Rejected => write!(f, "server rejected handshake"),
Self::NotFound => write!(f, "unknown token or tunnel closed"),
}
}
}
impl std::error::Error for HandshakeError {}
impl From<std::io::Error> for HandshakeError {
fn from(e: std::io::Error) -> Self { Self::Io(e) }
}
pub async fn client_handshake(
stream: &mut (impl AsyncWrite + AsyncRead + Unpin),
token: u64,
tunnel_type: TunnelType,
) -> Result<(), HandshakeError> {
let buf = encode_handshake(token, tunnel_type);
stream.write_all(&buf).await?;
let mut status = [0u8; 1];
stream.read_exact(&mut status).await?;
if status[0] == STATUS_OK {
Ok(())
} else {
Err(HandshakeError::Rejected)
}
}
pub async fn server_receive_handshake(
stream: &mut (impl AsyncRead + Unpin),
) -> Result<(u64, TunnelType), HandshakeError> {
let mut buf = [0u8; crate::HANDSHAKE_SIZE];
stream.read_exact(&mut buf).await?;
Ok(decode_handshake(&buf))
}
pub async fn server_accept(
stream: &mut (impl AsyncWrite + Unpin),
) -> Result<(), std::io::Error> {
stream.write_all(&[STATUS_OK]).await
}
pub async fn server_reject(
stream: &mut (impl AsyncWrite + Unpin),
reason: &str,
) -> Result<(), std::io::Error> {
let mut resp = vec![crate::STATUS_ERR];
let msg_bytes = reason.as_bytes();
resp.extend_from_slice(&(msg_bytes.len() as u16).to_be_bytes());
resp.extend_from_slice(msg_bytes);
stream.write_all(&resp).await
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignalKind {
NewConnection,
Disconnected,
}
pub async fn read_signal<R: AsyncRead + Unpin>(reader: &mut R) -> SignalKind {
let mut buf = [0u8; 1];
match reader.read_exact(&mut buf).await {
Ok(_) if buf[0] == crate::SIGNAL_NEW_CONN => SignalKind::NewConnection,
Ok(_) => SignalKind::NewConnection, Err(_) => SignalKind::Disconnected,
}
}
pub async fn send_notification<W: AsyncWrite + Unpin>(writer: &mut W) -> std::io::Result<()> {
writer.write_all(&[crate::SIGNAL_NEW_CONN]).await
}
pub async fn pipe_bidirectional(
mut a: tokio::net::TcpStream,
mut b: tokio::net::TcpStream,
) {
let (mut ar, mut aw) = a.split();
let (mut br, mut bw) = b.split();
tokio::select! {
_ = tokio::io::copy(&mut ar, &mut bw) => {}
_ = tokio::io::copy(&mut br, &mut aw) => {}
}
}