use std::net::SocketAddr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProxyVersion {
V1,
V2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProxyTransport {
Tcp,
Udp,
Unknown,
}
#[derive(Debug, Clone)]
pub struct ProxyTlv {
pub kind: u8,
pub value: Vec<u8>,
}
#[derive(Debug, Clone, Default)]
pub struct ProxyTlsInfo {
pub client_flags: u8,
pub verify: u32,
pub version: Option<String>,
pub common_name: Option<String>,
pub cipher: Option<String>,
pub sig_alg: Option<String>,
pub key_alg: Option<String>,
}
pub(crate) const MAX_PROXY_ADDR_LEN: usize = 536;
const PP2_TYPE_ALPN: u8 = 0x01;
const PP2_TYPE_AUTHORITY: u8 = 0x02;
pub(crate) const PP2_TYPE_CRC32C: u8 = 0x03;
const PP2_TYPE_NOOP: u8 = 0x04;
const PP2_TYPE_UNIQUE_ID: u8 = 0x05;
const PP2_TYPE_SSL: u8 = 0x20;
const PP2_SUBTYPE_SSL_VERSION: u8 = 0x21;
const PP2_SUBTYPE_SSL_CN: u8 = 0x22;
const PP2_SUBTYPE_SSL_CIPHER: u8 = 0x23;
const PP2_SUBTYPE_SSL_SIG_ALG: u8 = 0x24;
const PP2_SUBTYPE_SSL_KEY_ALG: u8 = 0x25;
const PP2_TYPE_NETNS: u8 = 0x30;
const PP2_TYPE_AWS_VPC_ENDPOINT_ID: u8 = 0xEA;
#[derive(Debug, Clone)]
pub struct ProxyHeader {
pub version: ProxyVersion,
pub transport: ProxyTransport,
pub source: Option<SocketAddr>,
pub destination: Option<SocketAddr>,
pub source_unix: Option<std::path::PathBuf>,
pub destination_unix: Option<std::path::PathBuf>,
pub authority: Option<String>,
pub alpn: Option<Vec<u8>>,
pub aws_vpc_endpoint_id: Option<String>,
pub tls: Option<ProxyTlsInfo>,
pub unique_id: Option<Vec<u8>>,
pub tlvs: Vec<ProxyTlv>,
pub crc32c_verified: Option<bool>,
}
impl ProxyHeader {
pub(crate) fn empty(version: ProxyVersion, transport: ProxyTransport) -> Self {
Self {
version,
transport,
source: None,
destination: None,
source_unix: None,
destination_unix: None,
authority: None,
alpn: None,
aws_vpc_endpoint_id: None,
tls: None,
unique_id: None,
tlvs: Vec::new(),
crc32c_verified: None,
}
}
}
pub(crate) fn apply_tlvs(header: &mut ProxyHeader, mut buf: &[u8]) {
while buf.len() >= 3 {
let kind = buf[0];
let len = u16::from_be_bytes([buf[1], buf[2]]) as usize;
if buf.len() < 3 + len {
break;
}
let value = &buf[3..3 + len];
match kind {
PP2_TYPE_ALPN => header.alpn = Some(value.to_vec()),
PP2_TYPE_AUTHORITY => {
if let Ok(s) = std::str::from_utf8(value) {
header.authority = Some(s.to_string());
}
}
PP2_TYPE_AWS_VPC_ENDPOINT_ID => {
if let Ok(s) = std::str::from_utf8(value) {
header.aws_vpc_endpoint_id = Some(s.to_string());
}
}
PP2_TYPE_UNIQUE_ID => header.unique_id = Some(value.to_vec()),
PP2_TYPE_SSL => {
#[allow(clippy::collapsible_match)]
if value.len() >= 5 {
let mut tls = ProxyTlsInfo {
client_flags: value[0],
verify: u32::from_be_bytes([value[1], value[2], value[3], value[4]]),
..Default::default()
};
let mut sub = &value[5..];
while sub.len() >= 3 {
let sk = sub[0];
let slen = u16::from_be_bytes([sub[1], sub[2]]) as usize;
if sub.len() < 3 + slen {
break;
}
let sval = &sub[3..3 + slen];
match sk {
PP2_SUBTYPE_SSL_VERSION => {
tls.version = std::str::from_utf8(sval).ok().map(str::to_string);
}
PP2_SUBTYPE_SSL_CN => {
tls.common_name = std::str::from_utf8(sval).ok().map(str::to_string);
}
PP2_SUBTYPE_SSL_CIPHER => {
tls.cipher = std::str::from_utf8(sval).ok().map(str::to_string);
}
PP2_SUBTYPE_SSL_SIG_ALG => {
tls.sig_alg = std::str::from_utf8(sval).ok().map(str::to_string);
}
PP2_SUBTYPE_SSL_KEY_ALG => {
tls.key_alg = std::str::from_utf8(sval).ok().map(str::to_string);
}
_ => {}
}
sub = &sub[3 + slen..];
}
header.tls = Some(tls);
}
}
PP2_TYPE_CRC32C | PP2_TYPE_NOOP | PP2_TYPE_NETNS => {}
_ => {}
}
header.tlvs.push(ProxyTlv {
kind,
value: value.to_vec(),
});
buf = &buf[3 + len..];
}
}