use crate::{Address, Command, Protocol, ProxyHdrV1, ProxyHdrV2};
use nom::{Parser, combinator::map_opt, number::streaming::be_u8};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::str::FromStr;
use tracing::debug;
impl Protocol {
fn new(input: u8) -> Option<Self> {
match input {
0x00 | 0x31 | 0x32 => Some(Self::Unspec),
0x11 => Some(Self::TcpV4),
0x12 => Some(Self::UdpV4),
0x21 => Some(Self::TcpV6),
0x22 => Some(Self::UdpV6),
_ => None,
}
}
}
impl Command {
fn new(input: u8) -> Option<Self> {
match input {
0x00 => Some(Self::Local),
0x01 => Some(Self::Proxy),
_ => None,
}
}
}
fn parse_bits(input: &[u8]) -> nom::IResult<&[u8], (u8, Command)> {
nom::bits::bits::<_, _, nom::error::Error<(&[u8], usize)>, _, _>((
nom::bits::streaming::tag(0x02, 4usize),
map_opt(nom::bits::streaming::take(4usize), Command::new),
))(input)
}
fn parse_addr_v4(input: &[u8]) -> nom::IResult<&[u8], Address> {
let (input, src_addr) = nom::number::complete::be_u32(input)?;
let (input, dst_addr) = nom::number::complete::be_u32(input)?;
let (input, src_port) = nom::number::complete::be_u16(input)?;
let (input, dst_port) = nom::number::complete::be_u16(input)?;
let src = SocketAddrV4::new(Ipv4Addr::from_bits(src_addr), src_port);
let dst = SocketAddrV4::new(Ipv4Addr::from_bits(dst_addr), dst_port);
Ok((input, Address::V4 { src, dst }))
}
fn parse_addr_v6(input: &[u8]) -> nom::IResult<&[u8], Address> {
let (input, src_addr) = nom::number::complete::be_u128(input)?;
let (input, dst_addr) = nom::number::complete::be_u128(input)?;
let (input, src_port) = nom::number::complete::be_u16(input)?;
let (input, dst_port) = nom::number::complete::be_u16(input)?;
let src = SocketAddrV6::new(Ipv6Addr::from_bits(src_addr), src_port, 0, 0);
let dst = SocketAddrV6::new(Ipv6Addr::from_bits(dst_addr), dst_port, 0, 0);
Ok((input, Address::V6 { src, dst }))
}
const SIGNATURE_V2: &[u8; 12] = b"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
pub(crate) fn parse_proxy_hdr_v2(input_data: &[u8]) -> nom::IResult<&[u8], ProxyHdrV2> {
let (input, _magic) = nom::bytes::streaming::tag(&SIGNATURE_V2[..])(input_data)
.inspect_err(|err| debug!(error=%err, "Missing Proxy v2 signature"))?;
let (input, (version, command)) = parse_bits(input)?;
if version != 2 {
debug!(version = version, "Invalid version expected 2");
return Err(nom::Err::Failure(nom::error::Error {
input,
code: nom::error::ErrorKind::Tag,
}));
}
let (input, protocol) = map_opt(be_u8, Protocol::new).parse(input)?;
let (input, length) = nom::number::streaming::be_u16(input)?;
let (remainder, input) = nom::bytes::streaming::take(length)(input)?;
let (_input, address) = match protocol {
Protocol::Unspec => (input, Address::None),
Protocol::TcpV4 | Protocol::UdpV4 => parse_addr_v4(input)?,
Protocol::TcpV6 | Protocol::UdpV6 => parse_addr_v6(input)?,
};
Ok((
remainder,
ProxyHdrV2 {
command,
protocol,
address,
},
))
}
#[cfg(any(feature = "tokio", test))]
pub const V1_MIN_LEN: usize = 32; pub const V1_MAX_LEN: usize = 107;
const V1_MAX_WORK_LEN: usize = V1_MAX_LEN - 6;
fn bytes_to_str(input: &[u8]) -> nom::IResult<&[u8], &str> {
str::from_utf8(input).map(|s| (input, s)).map_err(|_| {
nom::Err::Failure(nom::error::Error {
input,
code: nom::error::ErrorKind::AlphaNumeric,
})
})
}
pub(crate) fn parse_proxy_hdr_v1(input_data: &[u8]) -> nom::IResult<&[u8], ProxyHdrV1> {
let (input_data, _magic) = nom::bytes::streaming::tag("PROXY ")(input_data)
.inspect_err(|err| debug!(error=%err, "Missing Proxy v1 signature"))?;
let data_complete = input_data.len() > V1_MAX_WORK_LEN;
tracing::trace!(?data_complete, ?input_data);
let (ignore_crlf, working_data) = if data_complete {
let working_data = &input_data[..V1_MAX_WORK_LEN];
nom::character::complete::not_line_ending(working_data)?
} else {
nom::character::streaming::not_line_ending(input_data)?
};
let (_excess, ignore_crlf) = if data_complete {
nom::character::complete::crlf(ignore_crlf)?
} else {
nom::character::streaming::crlf(ignore_crlf)?
};
debug_assert!((ignore_crlf.len() + working_data.len()) <= input_data.len());
let remainder = &input_data[(2 + working_data.len())..];
let (input, protocol) = nom::bytes::complete::take_till(|c| c == 0x20)(working_data)?;
let protocol = if protocol == b"UNKNOWN" {
Protocol::Unspec
} else if protocol == b"TCP4" {
Protocol::TcpV4
} else if protocol == b"TCP6" {
Protocol::TcpV6
} else {
return Err(nom::Err::Failure(nom::error::Error {
input: protocol,
code: nom::error::ErrorKind::Tag,
}));
};
if input.is_empty() {
return Ok((
remainder,
ProxyHdrV1 {
protocol,
address: Address::None,
},
));
}
let (input, _discard) = nom::character::complete::space1(input)?;
let (input, src_addr_bytes) = nom::bytes::complete::take_till(|c| c == 0x20)(input)?;
let (_ignore, src_addr_str) = bytes_to_str(src_addr_bytes)?;
let (input, _discard) = nom::character::complete::space1(input)?;
let (input, dest_addr_bytes) = nom::bytes::complete::take_till(|c| c == 0x20)(input)?;
let (_ignore, dest_addr_str) = bytes_to_str(dest_addr_bytes)?;
let (input, _discard) = nom::character::complete::space1(input)?;
let (input, src_port_bytes) = nom::bytes::complete::take_till(|c| c == 0x20)(input)?;
let (_ignore, src_port_str) = bytes_to_str(src_port_bytes)?;
let (dest_port_bytes, _discard) = nom::character::complete::space1(input)?;
let (_ignore, dest_port_str) = bytes_to_str(dest_port_bytes)?;
let src_addr = IpAddr::from_str(src_addr_str).map_err(|_| {
nom::Err::Failure(nom::error::Error {
input: src_addr_bytes,
code: nom::error::ErrorKind::Satisfy,
})
})?;
let src_port = u16::from_str(src_port_str).map_err(|_| {
nom::Err::Failure(nom::error::Error {
input: src_port_bytes,
code: nom::error::ErrorKind::Satisfy,
})
})?;
let dest_addr = IpAddr::from_str(dest_addr_str).map_err(|_| {
nom::Err::Failure(nom::error::Error {
input: dest_addr_bytes,
code: nom::error::ErrorKind::Satisfy,
})
})?;
let dest_port = u16::from_str(dest_port_str).map_err(|_| {
nom::Err::Failure(nom::error::Error {
input: dest_port_bytes,
code: nom::error::ErrorKind::Satisfy,
})
})?;
let src_sock_addr = SocketAddr::new(src_addr, src_port);
let dest_sock_addr = SocketAddr::new(dest_addr, dest_port);
let address = match (src_sock_addr, dest_sock_addr) {
(SocketAddr::V4(src), SocketAddr::V4(dst)) => Address::V4 { src, dst },
(SocketAddr::V6(src), SocketAddr::V6(dst)) => Address::V6 { src, dst },
_ => Address::None,
};
Ok((remainder, ProxyHdrV1 { protocol, address }))
}
#[cfg(test)]
mod tests {
use crate::*;
use std::net::{SocketAddrV4, SocketAddrV6};
use std::str::FromStr;
#[test]
fn request_local() {
let _ = tracing_subscriber::fmt::try_init();
let sample =
hex::decode("0d0a0d0a000d0a515549540a20000007030004a9b87e8f").expect("valid hex");
let (took, hdr) = ProxyHdrV2::parse(sample.as_slice()).expect("should parse local addr");
tracing::debug!(?hdr);
assert_eq!(took, 23);
assert_eq!(hdr.command, Command::Local);
assert_eq!(hdr.protocol, Protocol::Unspec);
assert_eq!(hdr.address, Address::None);
}
#[test]
fn request_proxy_v4() {
let _ = tracing_subscriber::fmt::try_init();
let sample = hex::decode("0d0a0d0a000d0a515549540a2111000cac180c76ac180b8fcdcb027d")
.expect("valid hex");
let (took, hdr) = ProxyHdrV2::parse(sample.as_slice()).expect("should parse v4 addr");
tracing::debug!(?hdr);
assert_eq!(took, 28);
assert_eq!(hdr.command, Command::Proxy);
assert_eq!(hdr.protocol, Protocol::TcpV4);
assert_eq!(
hdr.address,
Address::V4 {
src: SocketAddrV4::from_str("172.24.12.118:52683").expect("valid addr"),
dst: SocketAddrV4::from_str("172.24.11.143:637").expect("valid addr"),
}
);
}
#[test]
fn request_proxy_v6() {
let _ = tracing_subscriber::fmt::try_init();
let sample = hex::decode("0d0a0d0a000d0a515549540a212100242403580b7d88001200000000000001fe2403580b7d8800110000000000001043d34c027d").expect("valid hex");
let (took, hdr) = ProxyHdrV2::parse(sample.as_slice()).expect("should parse v6 addr");
tracing::debug!(?hdr);
assert_eq!(took, 52);
assert_eq!(hdr.command, Command::Proxy);
assert_eq!(hdr.protocol, Protocol::TcpV6);
assert_eq!(
hdr.address,
Address::V6 {
src: SocketAddrV6::from_str("[2403:580b:7d88:12::1fe]:54092").expect("valid addr"),
dst: SocketAddrV6::from_str("[2403:580b:7d88:11::1043]:637").expect("valid addr"),
}
);
}
#[test]
fn request_proxyv1_v4_basic() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY TCP4 192.24.10.10 10.0.0.0 5789 80\r\nextra_data";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 42);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::TcpV4);
assert_eq!(
hdr.address,
Address::V4 {
src: SocketAddrV4::from_str("192.24.10.10:5789").unwrap(),
dst: SocketAddrV4::from_str("10.0.0.0:80").unwrap(),
}
);
}
#[test]
fn request_proxyv1_v4_basic_nodata() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY TCP4 192.24.10.10 10.0.0.0 5789 80\r\n";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 42);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::TcpV4);
assert_eq!(
hdr.address,
Address::V4 {
src: SocketAddrV4::from_str("192.24.10.10:5789").unwrap(),
dst: SocketAddrV4::from_str("10.0.0.0:80").unwrap(),
}
);
}
#[test]
fn request_proxyv1_v4_max() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n excess";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 56);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::TcpV4);
assert_eq!(
hdr.address,
Address::V4 {
src: SocketAddrV4::from_str("255.255.255.255:65535").unwrap(),
dst: SocketAddrV4::from_str("255.255.255.255:65535").unwrap(),
}
);
}
#[test]
fn request_proxyv1_v6_max() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n more extra data";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 104);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::TcpV6);
assert_eq!(
hdr.address,
Address::V6 {
src: SocketAddrV6::from_str("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")
.unwrap(),
dst: SocketAddrV6::from_str("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")
.unwrap(),
}
);
}
#[test]
fn request_proxyv1_unknown() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY UNKNOWN\r\n";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 15);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::Unspec);
assert_eq!(hdr.address, Address::None);
}
#[test]
fn request_proxyv1_v6_unknown() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n add extra data for luls";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 107);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::Unspec);
assert_eq!(
hdr.address,
Address::V6 {
src: SocketAddrV6::from_str("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")
.unwrap(),
dst: SocketAddrV6::from_str("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")
.unwrap(),
}
);
}
#[test]
fn request_proxyv1_incomplete() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY UNKNO";
let err = ProxyHdrV1::parse(data.as_bytes()).expect_err("Should fail!!!");
tracing::debug!(?err);
assert!(matches!(err, Error::Incomplete { .. }));
}
#[test]
fn request_proxyv1_v6_too_long() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535 too long\r\n";
let err = ProxyHdrV1::parse(data.as_bytes()).expect_err("Should fail!!!");
assert!(matches!(err, Error::Invalid));
}
#[test]
fn request_proxyv1_kanidm_4084() {
let _ = tracing_subscriber::fmt::try_init();
let data = "PROXY TCP4 91.221.138.33 91.221.138.106 47780 636\r\n";
let (took, hdr) = ProxyHdrV1::parse(data.as_bytes()).unwrap();
assert_eq!(took, 51);
tracing::debug!(?hdr);
assert_eq!(hdr.protocol, Protocol::TcpV4);
assert_eq!(
hdr.address,
Address::V4 {
src: SocketAddrV4::from_str("91.221.138.33:47780").unwrap(),
dst: SocketAddrV4::from_str("91.221.138.106:636").unwrap(),
}
);
}
}