#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[macro_use]
extern crate amplify;
mod encoding;
mod error;
use std::io;
use cypheraddr::{Host, HostName, NetAddr};
pub use error::ServerError;
use crate::encoding::{Encoding, EncodingError, DOMAIN, IPV4, IPV6};
#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum Error {
#[from]
Server(ServerError),
#[from]
Encoding(EncodingError),
InvalidReply,
VersionNotSupported(u8),
AuthRequired,
Completed,
Closed,
}
#[derive(Debug)]
pub enum Socks5 {
Initial(NetAddr<HostName>, bool),
Connected(NetAddr<HostName>),
Awaiting,
Reading(u8, u8),
Active(NetAddr<HostName>),
Rejected(ServerError, u8, u8),
Failed(Error),
}
impl Socks5 {
pub fn with(addr: impl Into<NetAddr<HostName>>, force_proxy: bool) -> Self {
Self::Initial(addr.into(), force_proxy)
}
pub fn advance(&mut self, input: &[u8]) -> Result<Vec<u8>, Error> {
match self {
Socks5::Initial(addr, false) if !addr.requires_proxy() => {
*self = Socks5::Active(addr.clone());
Ok(vec![])
}
Socks5::Initial(addr, _) => {
debug_assert!(input.is_empty());
let out = vec![0x05, 0x01, 0x00];
*self = Socks5::Connected(addr.clone());
Ok(out)
}
Socks5::Connected(addr) => {
debug_assert_eq!(input.len(), 2);
if input[0] != 0x05 {
*self = Socks5::Failed(Error::VersionNotSupported(input[0]));
return Err(Error::VersionNotSupported(input[0]));
}
if input[1] != 0x00 {
*self = Socks5::Failed(Error::AuthRequired);
return Err(Error::AuthRequired);
}
let mut out = vec![0x05, 0x01, 0x00];
addr.encode(&mut out)?;
*self = Socks5::Awaiting;
Ok(out)
}
Socks5::Awaiting => {
debug_assert_eq!(input.len(), 5);
if input[0] != 0x05 {
*self = Socks5::Failed(Error::VersionNotSupported(input[0]));
return Err(Error::VersionNotSupported(input[0]));
}
if input[1] != 0x00 {
let err = ServerError::from(input[1]);
*self = Socks5::Rejected(err, input[3], input[4]);
} else {
*self = Socks5::Reading(input[3], input[4]);
}
Ok(vec![])
}
Socks5::Reading(code1, code2) => {
let mut vec = Vec::with_capacity(input.len() + 2);
vec.extend_from_slice(&[*code1, *code2]);
vec.extend_from_slice(input);
let mut cursor = io::Cursor::new(vec);
let addr = NetAddr::<HostName>::decode(&mut cursor)?;
*self = Socks5::Active(addr);
Ok(vec![])
}
Socks5::Active(_) => Err(Error::Completed),
Socks5::Rejected(_, _, _) | Socks5::Failed(_) => Err(Error::Closed),
}
}
pub fn next_read_len(&self) -> usize {
match self {
Socks5::Initial(_, _) => 0,
Socks5::Connected(_) => 2,
Socks5::Awaiting => 5,
Socks5::Reading(ty, _) | Socks5::Rejected(_, ty, _) if *ty == IPV4 => 5,
Socks5::Reading(ty, _) | Socks5::Rejected(_, ty, _) if *ty == IPV6 => 17,
Socks5::Reading(ty, len) | Socks5::Rejected(_, ty, len) if *ty == DOMAIN => {
*len as usize + 1
}
Socks5::Reading(_, _) | Socks5::Rejected(_, _, _) => 1,
Socks5::Active(_) | Socks5::Failed(_) => 0,
}
}
}