use super::super::{ParsingError, SerializeError};
use bytes::{Buf, BufMut};
use std::net::SocketAddrV4;
#[derive(Debug, PartialEq)]
pub struct Request<'a>(pub &'a Address);
#[derive(Debug, PartialEq)]
pub struct Response(pub Status);
#[derive(Debug, PartialEq)]
pub enum Address {
Socket(SocketAddrV4),
Domain(String, u16),
}
#[derive(Debug, PartialEq)]
pub enum Status {
Success = 90,
Failed = 91,
IdentFailure = 92,
IdentMismatch = 93,
}
impl Request<'_> {
pub fn write_to_buf<B: BufMut>(&self, mut buf: B) -> Result<usize, SerializeError> {
match self.0 {
Address::Socket(socket) => {
if buf.remaining_mut() < 9 {
return Err(SerializeError::WouldOverflow);
}
buf.put_u8(0x04); buf.put_u8(0x01);
buf.put_u16(socket.port()); buf.put_slice(&socket.ip().octets());
buf.put_u8(0x00);
Ok(9)
}
Address::Domain(domain, port) => {
if buf.remaining_mut() < 9 + domain.len() + 1 {
return Err(SerializeError::WouldOverflow);
}
buf.put_u8(0x04); buf.put_u8(0x01);
buf.put_u16(*port); buf.put_slice(&[0x00, 0x00, 0x00, 0xFF]);
buf.put_u8(0x00);
buf.put_slice(domain.as_bytes()); buf.put_u8(0x00);
Ok(9 + domain.len() + 1)
}
}
}
}
impl TryFrom<&mut &[u8]> for Response {
type Error = ParsingError;
fn try_from(buf: &mut &[u8]) -> Result<Self, Self::Error> {
if buf.remaining() < 8 {
return Err(ParsingError::Incomplete);
}
if buf.get_u8() != 0x00 {
return Err(ParsingError::Other);
}
let status = buf.get_u8().try_into()?;
let _addr = {
let port = buf.get_u16();
let mut ip = [0; 4];
buf.copy_to_slice(&mut ip);
SocketAddrV4::new(ip.into(), port)
};
Ok(Self(status))
}
}
impl TryFrom<u8> for Status {
type Error = ParsingError;
fn try_from(byte: u8) -> Result<Self, Self::Error> {
Ok(match byte {
90 => Self::Success,
91 => Self::Failed,
92 => Self::IdentFailure,
93 => Self::IdentMismatch,
_ => return Err(ParsingError::Other),
})
}
}
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Success => "success",
Self::Failed => "server failed to execute command",
Self::IdentFailure => "server ident service failed",
Self::IdentMismatch => "server ident service did not recognise client identifier",
})
}
}
#[cfg(test)]
mod test {
use super::*;
use bytes::BytesMut;
use std::net::Ipv4Addr;
#[test]
fn request_serialization_with_socket() {
let expected = [
0x04, 0x01, 0x1F, 0x90, 127, 0, 0, 1, 0x00, ];
let addr = Address::Socket(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8080));
let mut buf = BytesMut::with_capacity(expected.len());
let n = Request(&addr).write_to_buf(&mut buf).unwrap();
assert_eq!(n, buf.len());
assert_eq!(&buf[..], &expected[..]);
}
#[test]
fn request_serialization_with_domain() {
let expected = [
0x04, 0x01, 0x1F, 0x90, 0x00, 0x00, 0x00, 0xFF, 0x00, b'e', b'x', b'a', b'm', b'p', b'l', b'e', b'.', b'c', b'o', b'm', 0x00, ];
let addr = Address::Domain("example.com".into(), 8080);
let mut buf = BytesMut::with_capacity(expected.len());
let n = Request(&addr).write_to_buf(&mut buf).unwrap();
assert_eq!(n, buf.len());
assert_eq!(&buf[..], &expected[..]);
}
#[test]
fn response_deserialization() {
let raw = [
0x00, 90, 0x1F, 0x90, 127, 0, 0, 1, ];
let mut view = &raw[..];
let res = Response::try_from(&mut view).unwrap();
assert_eq!(res, Response(Status::Success));
assert!(view.is_empty());
}
#[test]
fn response_incomplete() {
let raw = [
0x00, 90, 0x00, ];
let err = Response::try_from(&mut &raw[..]).unwrap_err();
assert!(matches!(err, ParsingError::Incomplete));
}
#[test]
fn request_serialization_would_overflow() {
let addr = Address::Socket(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8080));
let mut short = [0u8; 5];
let err = Request(&addr).write_to_buf(&mut short[..]).unwrap_err();
assert!(matches!(err, SerializeError::WouldOverflow));
}
}