#[derive(Debug, thiserror::Error)]
pub enum Socks5Error {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("unsupported SOCKS version: {0}")]
UnsupportedVersion(u8),
#[error("unsupported command: {0}")]
UnsupportedCommand(u8),
#[error("unsupported address type: {0}")]
UnsupportedAddressType(u8),
#[error("unsupported authentication method: {0}")]
UnsupportedAuthMethod(u8),
#[error("authentication failed")]
AuthFailed,
#[error("credentials too long (max 255 bytes)")]
CredentialsTooLong,
#[error("connection refused by SOCKS server")]
ConnectionRefused,
#[error("connection failed: {0}")]
ConnectionFailed(String),
#[error("malformed message: {0}")]
MalformedMessage(String),
#[error("method negotiation failed")]
MethodNegotiationFailed,
#[error("unexpected end of stream")]
UnexpectedEof,
#[error("address too long")]
AddressTooLong,
#[error("non-zero reserved byte: {0}")]
InvalidReservedByte(u8),
#[error("domain exceeds SOCKS5 255-byte limit: {0}")]
DomainTooLong(usize),
}
impl From<Socks5Error> for std::io::Error {
fn from(e: Socks5Error) -> Self {
match e {
Socks5Error::Io(io_err) => io_err,
other => std::io::Error::other(other),
}
}
}
impl Socks5Error {
pub fn display_hex(&self) -> String {
match self {
Socks5Error::UnsupportedVersion(v) => {
format!("unsupported SOCKS version: {v:#04x}")
}
Socks5Error::UnsupportedCommand(c) => {
format!("unsupported command: {c:#04x}")
}
Socks5Error::UnsupportedAddressType(a) => {
format!("unsupported address type: {a:#04x}")
}
Socks5Error::UnsupportedAuthMethod(m) => {
format!("unsupported authentication method: {m:#04x}")
}
_ => self.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
assert_eq!(
Socks5Error::UnsupportedVersion(0x04).to_string(),
"unsupported SOCKS version: 4"
);
assert_eq!(
Socks5Error::UnsupportedCommand(0x02).to_string(),
"unsupported command: 2"
);
assert_eq!(
Socks5Error::UnsupportedAddressType(0x05).to_string(),
"unsupported address type: 5"
);
assert_eq!(
Socks5Error::UnsupportedAuthMethod(0x03).to_string(),
"unsupported authentication method: 3"
);
assert_eq!(Socks5Error::AuthFailed.to_string(), "authentication failed");
assert_eq!(
Socks5Error::CredentialsTooLong.to_string(),
"credentials too long (max 255 bytes)"
);
assert_eq!(
Socks5Error::ConnectionRefused.to_string(),
"connection refused by SOCKS server"
);
assert_eq!(
Socks5Error::ConnectionFailed("timeout".to_string()).to_string(),
"connection failed: timeout"
);
assert_eq!(
Socks5Error::MalformedMessage("bad".to_string()).to_string(),
"malformed message: bad"
);
assert_eq!(
Socks5Error::MethodNegotiationFailed.to_string(),
"method negotiation failed"
);
assert_eq!(
Socks5Error::UnexpectedEof.to_string(),
"unexpected end of stream"
);
assert_eq!(Socks5Error::AddressTooLong.to_string(), "address too long");
assert_eq!(
Socks5Error::InvalidReservedByte(0x01).to_string(),
"non-zero reserved byte: 1"
);
assert_eq!(
Socks5Error::DomainTooLong(256).to_string(),
"domain exceeds SOCKS5 255-byte limit: 256"
);
}
#[test]
fn test_error_display_hex() {
assert_eq!(
Socks5Error::UnsupportedVersion(0x04).display_hex(),
"unsupported SOCKS version: 0x04"
);
assert_eq!(
Socks5Error::UnsupportedCommand(0x02).display_hex(),
"unsupported command: 0x02"
);
assert_eq!(
Socks5Error::UnsupportedAddressType(0x05).display_hex(),
"unsupported address type: 0x05"
);
assert_eq!(
Socks5Error::UnsupportedAuthMethod(0x03).display_hex(),
"unsupported authentication method: 0x03"
);
assert_eq!(
Socks5Error::AuthFailed.display_hex(),
"authentication failed"
);
}
}