use std::{borrow::Cow, convert::TryFrom, str}; const PP2_TYPE_ALPN: u8 = 0x01; const PP2_TYPE_AUTHORITY: u8 = 0x02; const PP2_TYPE_CRC32C: u8 = 0x03; // done const PP2_TYPE_NOOP: u8 = 0x04; // done const PP2_TYPE_UNIQUE_ID: u8 = 0x05; // done 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; pub trait Tlv: Sized { const TYPE: u8; fn try_from_value(value: &[u8]) -> Option; fn value_bytes(&self) -> Cow<'_, [u8]>; fn try_from_parts(typ: u8, value: &[u8]) -> Option { if typ != Self::TYPE { return None; } Self::try_from_value(value) } } /// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence defining /// the upper layer protocol in use over the connection. The most common use case /// will be to pass the exact copy of the ALPN extension of the Transport Layer /// Security (TLS) protocol as defined by RFC7301 [9]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Alpn { alpn: Vec, } impl Alpn { /// /// /// # Panics /// Panics if `alpn` is empty (i.e., has length of 0). pub fn new(alpn: impl Into>) -> Self { let alpn = alpn.into(); assert!(!alpn.is_empty(), "ALPN TLV value cannot be empty"); Self { alpn } } } impl Tlv for Alpn { const TYPE: u8 = PP2_TYPE_ALPN; fn try_from_value(value: &[u8]) -> Option { Some(Self { alpn: value.to_owned(), }) } fn value_bytes(&self) -> Cow<'_, [u8]> { Cow::Borrowed(&self.alpn) } } /// Contains the host name value passed by the client, as an UTF8-encoded string. /// In case of TLS being used on the client connection, this is the exact copy of /// the "server_name" extension as defined by RFC3546 [10], section 3.1, often /// referred to as "SNI". There are probably other situations where an authority /// can be mentioned on a connection without TLS being involved at all. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Authority { authority: String, } impl Authority { /// A UTF-8 /// /// # Panics /// Panics if `authority` is an empty string. pub fn new(authority: impl Into) -> Self { let authority = authority.into(); assert!(!authority.is_empty(), "Authority TLV value cannot be empty"); Self { authority } } } impl Tlv for Authority { const TYPE: u8 = PP2_TYPE_AUTHORITY; fn try_from_value(value: &[u8]) -> Option { Some(Self { authority: str::from_utf8(value).ok()?.to_owned(), }) } fn value_bytes(&self) -> Cow<'_, [u8]> { Cow::Borrowed(&self.authority.as_bytes()) } } /// The value of the type PP2_TYPE_CRC32C is a 32-bit number storing the CRC32c /// checksum of the PROXY protocol header. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Crc32c { pub(crate) checksum: u32, } impl Tlv for Crc32c { const TYPE: u8 = PP2_TYPE_CRC32C; fn try_from_value(value: &[u8]) -> Option { let checksum_bytes = <[u8; 4]>::try_from(value).ok()?; Some(Self { checksum: u32::from_be_bytes(checksum_bytes), }) } fn value_bytes(&self) -> Cow<'_, [u8]> { Cow::Owned(self.checksum.to_be_bytes().to_vec()) } } /// The TLV of this type should be ignored when parsed. The value is zero or more /// bytes. Can be used for data padding or alignment. Note that it can be used /// to align only by 3 or more bytes because a TLV can not be smaller than that. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Noop { value: Vec, } impl Noop { /// /// /// # Panics /// Panics if `value` is empty (i.e., has length of 0). pub fn new(value: impl Into>) -> Self { let value = value.into(); assert!(!value.is_empty(), "Noop TLV `value` cannot be empty"); Self { value } } } impl Tlv for Noop { const TYPE: u8 = PP2_TYPE_NOOP; fn try_from_value(value: &[u8]) -> Option { Some(Self { value: value.to_owned(), }) } fn value_bytes(&self) -> Cow<'_, [u8]> { Cow::Borrowed(&self.value) } } /// The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to /// 128 bytes generated by the upstream proxy that uniquely identifies the /// connection. /// /// The unique ID can be used to easily correlate connections across multiple /// layers of proxies, without needing to look up IP addresses and port numbers. #[derive(Debug, Clone, PartialEq, Eq)] pub struct UniqueId { value: Vec, } impl UniqueId { /// /// /// # Panics /// Panics if `value` is empty (i.e., has length of 0). pub fn new(id: impl Into>) -> Self { let value = id.into(); assert!(!value.is_empty(), "UniqueId TLV `value` cannot be empty"); Self { value } } } impl Tlv for UniqueId { const TYPE: u8 = PP2_TYPE_UNIQUE_ID; fn try_from_value(value: &[u8]) -> Option { Some(Self { value: value.to_owned(), }) } fn value_bytes(&self) -> Cow<'_, [u8]> { Cow::Borrowed(&self.value) } } #[cfg(test)] mod tests { use super::*; // #[test] // #[should_panic] // fn tlv_zero_len() { // Tlv::new(0x00, vec![]); // } #[test] fn tlv_as_crc32c() { // noop assert_eq!(Crc32c::try_from_parts(0x04, &[0x00]), None); assert_eq!( Crc32c::try_from_parts(0x03, &[0x08, 0x70, 0x17, 0x7b]), Some(Crc32c { checksum: 141563771 }) ); } }