use std::net::{IpAddr, SocketAddr};
use crate::error::{InvalidReason, ParseError};
use crate::types::{
AddressFamily, Command, ProxyAddress, ProxyInfo, Tlvs, Transport, TransportProtocol, Version,
};
const V1_MAX_LEN: usize = 107;
pub(crate) fn parse_v1(buf: &[u8]) -> Result<(ProxyInfo, usize), ParseError> {
let search_end = buf.len().min(V1_MAX_LEN);
let line_end = buf[..search_end]
.windows(2)
.position(|w| w == b"\r\n")
.ok_or(if buf.len() < V1_MAX_LEN {
ParseError::Incomplete
} else {
ParseError::Invalid(InvalidReason::V1TooLong)
})?;
let line = std::str::from_utf8(&buf[..line_end])
.map_err(|_| ParseError::Invalid(InvalidReason::V1NotUtf8))?;
let consumed = line_end + 2;
if let Some(rest) = line.strip_prefix("PROXY UNKNOWN")
&& (rest.is_empty() || rest.starts_with(' '))
{
return Ok((
ProxyInfo {
version: Version::V1,
command: Command::Proxy,
transport: None,
source: None,
destination: None,
tlvs: Tlvs::default(),
},
consumed,
));
}
let mut parts = line.splitn(7, ' ');
let _proxy = parts.next();
let transport_str = parts
.next()
.ok_or(ParseError::Invalid(InvalidReason::V1InvalidFormat))?;
let src_ip_str = parts
.next()
.ok_or(ParseError::Invalid(InvalidReason::V1InvalidFormat))?;
let dst_ip_str = parts
.next()
.ok_or(ParseError::Invalid(InvalidReason::V1InvalidFormat))?;
let src_port_str = parts
.next()
.ok_or(ParseError::Invalid(InvalidReason::V1InvalidFormat))?;
let dst_port_str = parts
.next()
.ok_or(ParseError::Invalid(InvalidReason::V1InvalidFormat))?;
if parts.next().is_some() {
return Err(ParseError::Invalid(InvalidReason::V1InvalidFormat));
}
let (family, protocol) = match transport_str {
"TCP4" => (AddressFamily::Inet, TransportProtocol::Stream),
"TCP6" => (AddressFamily::Inet6, TransportProtocol::Stream),
other => {
return Err(ParseError::Invalid(InvalidReason::V1UnknownTransport(
other.to_string(),
)));
}
};
let src_ip: IpAddr = src_ip_str
.parse()
.map_err(|_| ParseError::Invalid(InvalidReason::InvalidIp(src_ip_str.to_string())))?;
let dst_ip: IpAddr = dst_ip_str
.parse()
.map_err(|_| ParseError::Invalid(InvalidReason::InvalidIp(dst_ip_str.to_string())))?;
match (&family, &src_ip) {
(AddressFamily::Inet, IpAddr::V4(_)) => {}
(AddressFamily::Inet6, IpAddr::V6(_)) => {}
_ => {
return Err(ParseError::Invalid(InvalidReason::InvalidIp(
src_ip_str.to_string(),
)));
}
}
match (&family, &dst_ip) {
(AddressFamily::Inet, IpAddr::V4(_)) => {}
(AddressFamily::Inet6, IpAddr::V6(_)) => {}
_ => {
return Err(ParseError::Invalid(InvalidReason::InvalidIp(
dst_ip_str.to_string(),
)));
}
}
let src_port: u16 = src_port_str
.parse()
.map_err(|_| ParseError::Invalid(InvalidReason::InvalidPort(src_port_str.to_string())))?;
let dst_port: u16 = dst_port_str
.parse()
.map_err(|_| ParseError::Invalid(InvalidReason::InvalidPort(dst_port_str.to_string())))?;
Ok((
ProxyInfo {
version: Version::V1,
command: Command::Proxy,
transport: Some(Transport { family, protocol }),
source: Some(ProxyAddress::Inet(SocketAddr::new(src_ip, src_port))),
destination: Some(ProxyAddress::Inet(SocketAddr::new(dst_ip, dst_port))),
tlvs: Tlvs::default(),
},
consumed,
))
}