s5x 0.1.0

socks5 proxy server
use std::net::IpAddr;

use tokio::net::TcpStream;

use crate::prelude::*;

pub fn addr_to_bytes(addr: &TcpStream) -> Result<Vec<u8>, AppError> {
    let addr = addr.local_addr()?;

    let mut ip_as_bytes = match addr.ip() {
        IpAddr::V4(ipv4) => ipv4.octets().to_vec(),
        IpAddr::V6(ipv6) => ipv6.octets().to_vec(),
    };
    let port_as_bytes: [u8; 2] = addr.port().to_be_bytes();
    ip_as_bytes.extend_from_slice(&port_as_bytes);

    Ok(ip_as_bytes)
}

pub fn bytes_to_credentials(buf: &[u8]) -> Result<(String, String), AppError> {
    let mut bytes = buf.iter();

    let _ver = bytes.next().ok_or(AppError::AuthFailed)?;
    let ulen = *bytes.next().ok_or(AppError::AuthFailed)? as usize;
    let user: Vec<u8> = bytes.by_ref().take(ulen).copied().collect();
    let plen = *bytes.next().ok_or(AppError::AuthFailed)? as usize;
    let pass: Vec<u8> = bytes.take(plen).copied().collect();

    let user = String::from_utf8(user).map_err(|_| AppError::AuthFailed)?;
    let pass = String::from_utf8(pass).map_err(|_| AppError::AuthFailed)?;

    debug!(username = ?user, password = ?pass, "auth");

    Ok((user, pass))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_bytes_to_credentials() {
        let buf = &[
            0x01,
            0x04,
            b'u', b's', b'e', b'r',
            0x06,
            b'p', b'a', b's', b's', b'w', b'd'
        ];
        let credentials = bytes_to_credentials(buf);
        assert!(credentials.is_ok());
        let (user, pass) = credentials.unwrap();
        assert_eq!(user, "user");
        assert_eq!(pass, "passwd");
    }
}