#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![warn(clippy::all)]
pub mod version1;
pub mod version2;
use bytes::{Buf, BytesMut};
use snafu::{ensure, ResultExt as _, Snafu};
#[derive(Debug, Snafu)]
#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub enum ParseError {
#[snafu(display("the given data is not a PROXY header"))]
NotProxyHeader,
#[snafu(display("the version {} is invalid", version))]
InvalidVersion { version: u32 },
#[snafu(display("there was an error while parsing the v1 header: {}", source))]
Version1 { source: version1::ParseError },
#[snafu(display("there was an error while parsing the v2 header: {}", source))]
Version2 { source: version2::ParseError },
}
#[derive(Debug, Snafu)]
#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)]
pub enum EncodeError {
#[snafu(display("there was an error while encoding the v1 header: {}", source))]
WriteVersion1 { source: version1::EncodeError },
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)]
pub enum ProxyHeader {
Version1 {
addresses: version1::ProxyAddresses,
},
Version2 {
command: version2::ProxyCommand,
transport_protocol: version2::ProxyTransportProtocol,
addresses: version2::ProxyAddresses,
},
}
fn parse_version(buf: &mut impl Buf) -> Result<u32, ParseError> {
ensure!(buf.remaining() >= 6, NotProxyHeader);
if buf.chunk()[..6] == [b'P', b'R', b'O', b'X', b'Y', b' '] {
buf.advance(6);
return Ok(1);
}
ensure!(buf.remaining() >= 13, NotProxyHeader);
ensure!(
buf.chunk()[..12]
== [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A],
NotProxyHeader
);
buf.advance(12);
let version = buf.chunk()[0];
let version = version >> 4;
if version == 1 {
return InvalidVersion { version: 1u32 }.fail();
}
Ok(version as u32)
}
pub fn parse(buf: &mut impl Buf) -> Result<ProxyHeader, ParseError> {
let version = match parse_version(buf) {
Ok(ver) => ver,
Err(e) => return Err(e),
};
Ok(match version {
1 => self::version1::parse(buf).context(Version1)?,
2 => self::version2::parse(buf).context(Version2)?,
_ => return InvalidVersion { version }.fail(),
})
}
pub fn encode(header: ProxyHeader) -> Result<BytesMut, EncodeError> {
Ok(match header {
ProxyHeader::Version1 { addresses, .. } => {
version1::encode(addresses).context(WriteVersion1)?
}
ProxyHeader::Version2 {
command,
transport_protocol,
addresses,
} => version2::encode(command, transport_protocol, addresses),
#[allow(unreachable_patterns)]
_ => unimplemented!("Unimplemented version?"),
})
}
#[cfg(test)]
mod parse_tests {
use super::*;
use crate::ProxyHeader;
use bytes::Bytes;
use pretty_assertions::assert_eq;
use rand::prelude::*;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
#[test]
fn test_version1() {
let unknown = Ok(ProxyHeader::Version1 {
addresses: version1::ProxyAddresses::Unknown,
});
assert_eq!(parse(&mut &b"PROXY UNKNOWN\r\n"[..]), unknown);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN this is bogus data!\r\r\r\n"[..]),
unknown,
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN 192.168.0.1 192.168.1.1 123 321\r\n"[..]),
unknown,
);
let mut random = [0u8; 128];
rand::thread_rng().fill_bytes(&mut random);
let mut header = b"PROXY UNKNOWN ".to_vec();
header.extend(&random[..]);
header.extend(b"\r\n");
let mut buf = Bytes::from(header);
assert_eq!(parse(&mut buf), unknown);
assert!(!buf.has_remaining());
fn valid_v4(
(a, b, c, d): (u8, u8, u8, u8),
e: u16,
(f, g, h, i): (u8, u8, u8, u8),
j: u16,
) -> ProxyHeader {
ProxyHeader::Version1 {
addresses: version1::ProxyAddresses::Ipv4 {
source: SocketAddrV4::new(Ipv4Addr::new(a, b, c, d), e),
destination: SocketAddrV4::new(Ipv4Addr::new(f, g, h, i), j),
},
}
}
assert_eq!(
parse(&mut &b"PROXY TCP4 192.168.201.102 1.2.3.4 0 65535\r\n"[..]),
Ok(valid_v4((192, 168, 201, 102), 0, (1, 2, 3, 4), 65535)),
);
assert_eq!(
parse(&mut &b"PROXY TCP4 0.0.0.0 0.0.0.0 0 0\r\n"[..]),
Ok(valid_v4((0, 0, 0, 0), 0, (0, 0, 0, 0), 0)),
);
assert_eq!(
parse(&mut &b"PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"[..]),
Ok(valid_v4(
(255, 255, 255, 255),
65535,
(255, 255, 255, 255),
65535,
)),
);
fn valid_v6(
(a, b, c, d, e, f, g, h): (u16, u16, u16, u16, u16, u16, u16, u16),
i: u16,
(j, k, l, m, n, o, p, q): (u16, u16, u16, u16, u16, u16, u16, u16),
r: u16,
) -> ProxyHeader {
ProxyHeader::Version1 {
addresses: version1::ProxyAddresses::Ipv6 {
source: SocketAddrV6::new(Ipv6Addr::new(a, b, c, d, e, f, g, h), i, 0, 0),
destination: SocketAddrV6::new(Ipv6Addr::new(j, k, l, m, n, o, p, q), r, 0, 0),
},
}
}
assert_eq!(
parse(&mut &b"PROXY TCP6 ab:ce:ef:01:23:45:67:89 ::1 0 65535\r\n"[..]),
Ok(valid_v6(
(0xAB, 0xCE, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89),
0,
(0, 0, 0, 0, 0, 0, 0, 1),
65535,
)),
);
assert_eq!(
parse(&mut &b"PROXY TCP6 :: :: 0 0\r\n"[..]),
Ok(valid_v6(
(0, 0, 0, 0, 0, 0, 0, 0),
0,
(0, 0, 0, 0, 0, 0, 0, 0),
0,
)),
);
assert_eq!(
parse(
&mut &b"PROXY TCP6 ff:ff:ff:ff:ff:ff:ff:ff ff:ff:ff:ff:ff:ff:ff:ff 65535 65535\r\n"
[..],
),
Ok(valid_v6(
(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF),
65535,
(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF),
65535,
)),
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN \r"[..]),
Err(ParseError::Version1 {
source: version1::ParseError::UnexpectedEof,
}),
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN \r\t\t\r"[..]),
Err(ParseError::Version1 {
source: version1::ParseError::UnexpectedEof,
}),
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN\r\r\r\r\rHello, world!"[..]),
Err(ParseError::Version1 {
source: version1::ParseError::UnexpectedEof,
}),
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN\nGET /index.html HTTP/1.0"[..]),
Err(ParseError::Version1 {
source: version1::ParseError::UnexpectedEof,
}),
);
assert_eq!(
parse(&mut &b"PROXY UNKNOWN\n"[..]),
Err(ParseError::Version1 {
source: version1::ParseError::UnexpectedEof,
}),
);
}
#[test]
fn test_version2() {
const PREFIX_LOCAL: [u8; 13] = [
0x0D,
0x0A,
0x0D,
0x0A,
0x00,
0x0D,
0x0A,
0x51,
0x55,
0x49,
0x54,
0x0A,
(2 << 4) | 0,
];
const PREFIX_PROXY: [u8; 13] = [
0x0D,
0x0A,
0x0D,
0x0A,
0x00,
0x0D,
0x0A,
0x51,
0x55,
0x49,
0x54,
0x0A,
(2 << 4) | 1,
];
assert_eq!(
parse(&mut [&PREFIX_LOCAL[..], &[0u8; 16][..]].concat().as_slice()),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Local,
addresses: version2::ProxyAddresses::Unspec,
transport_protocol: version2::ProxyTransportProtocol::Unspec,
}),
);
assert_eq!(
parse(&mut [&PREFIX_PROXY[..], &[0u8; 16][..]].concat().as_slice()),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Proxy,
addresses: version2::ProxyAddresses::Unspec,
transport_protocol: version2::ProxyTransportProtocol::Unspec,
}),
);
assert_eq!(
parse(
&mut [
&PREFIX_PROXY[..],
&[
(1 << 4) | 1,
0,
15,
127,
0,
0,
1,
192,
168,
0,
1,
255,
255,
1,
1,
69,
0,
0,
][..]
]
.concat()
.as_slice(),
),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Proxy,
transport_protocol: version2::ProxyTransportProtocol::Stream,
addresses: version2::ProxyAddresses::Ipv4 {
source: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 65535),
destination: SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 257),
},
})
);
let mut data = Bytes::from(
[
&PREFIX_LOCAL[..],
&[
(1 << 4) | 2,
0,
12,
0,
0,
0,
0,
255,
255,
255,
255,
0,
0,
255,
0,
1,
2,
3,
4,
][..],
]
.concat(),
);
assert_eq!(
parse(&mut data),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Local,
transport_protocol: version2::ProxyTransportProtocol::Datagram,
addresses: version2::ProxyAddresses::Ipv4 {
source: SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0),
destination: SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), 255 << 8),
},
})
);
assert!(data.remaining() == 4);
assert_eq!(
parse(
&mut [
&PREFIX_PROXY[..],
&[
(2 << 4) | 2,
0,
39,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
1,
1,
69,
0,
0,
][..],
]
.concat()
.as_slice(),
),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Proxy,
transport_protocol: version2::ProxyTransportProtocol::Datagram,
addresses: version2::ProxyAddresses::Ipv6 {
source: SocketAddrV6::new(
Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535),
65535,
0,
0,
),
destination: SocketAddrV6::new(
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
257,
0,
0,
)
},
})
);
let mut data = Bytes::from(
[
&PREFIX_LOCAL[..],
&[
(2 << 4) | 1,
0,
36,
81,
92,
0,
52,
83,
12,
255,
68,
19,
5,
111,
200,
54,
90,
55,
66,
255,
255,
255,
255,
0,
0,
0,
0,
123,
123,
69,
69,
21,
21,
42,
42,
123,
0,
255,
255,
1,
2,
3,
4,
][..],
]
.concat(),
);
assert_eq!(
parse(&mut data),
Ok(ProxyHeader::Version2 {
command: version2::ProxyCommand::Local,
transport_protocol: version2::ProxyTransportProtocol::Stream,
addresses: version2::ProxyAddresses::Ipv6 {
source: SocketAddrV6::new(
Ipv6Addr::new(20828, 52, 21260, 65348, 4869, 28616, 13914, 14146),
31488,
0,
0,
),
destination: SocketAddrV6::new(
Ipv6Addr::new(65535, 65535, 0, 0, 31611, 17733, 5397, 10794),
65535,
0,
0,
),
},
})
);
assert!(data.remaining() == 4);
let mut data = [0u8; 200];
rand::thread_rng().fill_bytes(&mut data);
data[0] = 99;
assert!(parse(&mut &data[..]).is_err());
assert_eq!(
parse(&mut &PREFIX_LOCAL[..]),
Err(ParseError::Version2 {
source: version2::ParseError::UnexpectedEof,
}),
);
assert_eq!(
parse(
&mut [
&PREFIX_PROXY[..],
&[
(1 << 4) | 1,
0,
3,
][..],
]
.concat()
.as_slice(),
),
Err(ParseError::Version2 {
source: version2::ParseError::InsufficientLengthSpecified {
given: 3,
needs: 4 * 2 + 2 * 2,
},
}),
);
}
#[test]
fn test_unknown_version() {
assert_eq!(
parse_version(
&mut &[
0x0D,
0x0A,
0x0D,
0x0A,
0x00,
0x0D,
0x0A,
0x51,
0x55,
0x49,
0x54,
0x0A,
1 << 4,
][..],
),
Err(ParseError::InvalidVersion { version: 1 }),
);
}
#[test]
fn test_version_parsing_correct() {
assert_eq!(
parse_version(&mut &[b'P', b'R', b'O', b'X', b'Y', b' '][..]),
Ok(1),
);
assert_eq!(
parse_version(
&mut &[
0x0D,
0x0A,
0x0D,
0x0A,
0x00,
0x0D,
0x0A,
0x51,
0x55,
0x49,
0x54,
0x0A,
15 << 4,
][..],
),
Ok(15),
);
}
#[test]
fn test_version_parsing_errors() {
assert_eq!(
parse_version(&mut &b"Proximyst"[..]),
Err(ParseError::NotProxyHeader)
);
}
}