use bytes::{BufMut, BytesMut};
use socks5_impl::protocol::{Address, AsyncStreamOperation, StreamOperation};
use tokio::io::{AsyncRead, AsyncReadExt};
pub const V2_MAGIC_ADDRESS: &str = "sp.v2.udp-over-tcp.arpa";
#[derive(Clone, Debug)]
pub struct Request {
pub is_connect: bool,
pub destination: Address,
}
pub fn request_destination() -> Address {
Address::DomainAddress(V2_MAGIC_ADDRESS.into(), 0)
}
pub fn is_request_destination(address: &Address) -> bool {
matches!(address, Address::DomainAddress(domain, _) if &**domain == V2_MAGIC_ADDRESS)
}
pub fn encode_request(request: &Request) -> Vec<u8> {
let mut buf = BytesMut::with_capacity(1 + request.destination.len());
buf.put_u8(u8::from(request.is_connect));
request.destination.write_to_buf(&mut buf);
buf.to_vec()
}
pub async fn read_request<R>(reader: &mut R) -> std::io::Result<Request>
where
R: AsyncRead + Unpin + Send + ?Sized,
{
let is_connect = reader.read_u8().await? != 0;
let destination = Address::retrieve_from_async_stream(reader).await?;
Ok(Request { is_connect, destination })
}
pub fn encode_non_connect_packet(destination: &Address, payload: &[u8]) -> std::io::Result<Vec<u8>> {
if payload.len() > u16::MAX as usize {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "UOT packet too large"));
}
let mut buf = BytesMut::with_capacity(destination.len() + 2 + payload.len());
destination.write_to_buf(&mut buf);
buf.put_u16(payload.len() as u16);
buf.extend_from_slice(payload);
Ok(buf.to_vec())
}
pub async fn read_non_connect_packet<R>(reader: &mut R) -> std::io::Result<(Address, Vec<u8>)>
where
R: AsyncRead + Unpin + Send + ?Sized,
{
let destination = Address::retrieve_from_async_stream(reader).await?;
let payload_len = reader.read_u16().await? as usize;
let mut payload = vec![0u8; payload_len];
reader.read_exact(&mut payload).await?;
Ok((destination, payload))
}