diff --git a/actix-proxy-protocol/examples/proxy-server.rs b/actix-proxy-protocol/examples/proxy-server.rs index 6bbb9243..92062b6b 100644 --- a/actix-proxy-protocol/examples/proxy-server.rs +++ b/actix-proxy-protocol/examples/proxy-server.rs @@ -76,7 +76,9 @@ async fn wrap_with_proxy_protocol_v2(mut stream: TcpStream) -> io::Result<()> { proxy_header.add_typed_tlv(tlv::UniqueId::new("4269")); // UNIQUE_ID proxy_header.add_typed_tlv(tlv::Noop::new("NOOP m8")); // NOOP - proxy_header.add_crc23c_checksum_tlv(); + proxy_header.add_typed_tlv(tlv::Authority::new("localhost")); // NOOP + proxy_header.add_typed_tlv(tlv::Alpn::new("http/1.1")); // NOOP + proxy_header.add_crc23c_checksum(); proxy_header.write_to_tokio(&mut upstream).await?; diff --git a/actix-proxy-protocol/src/tlv.rs b/actix-proxy-protocol/src/tlv.rs index e2e001ab..3de965c3 100644 --- a/actix-proxy-protocol/src/tlv.rs +++ b/actix-proxy-protocol/src/tlv.rs @@ -1,4 +1,17 @@ -use std::{borrow::Cow, convert::TryFrom}; +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; @@ -16,13 +29,90 @@ pub trait Tlv: Sized { } } +/// 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 = 0x03; + const TYPE: u8 = PP2_TYPE_CRC32C; fn try_from_value(value: &[u8]) -> Option { let checksum_bytes = <[u8; 4]>::try_from(value).ok()?; @@ -37,7 +127,10 @@ impl Tlv for Crc32c { } } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +/// 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, } @@ -46,7 +139,7 @@ impl Noop { /// /// /// # Panics - /// Panics if value is empty (i.e., has length of 0). + /// Panics if `value` is empty (i.e., has length of 0). pub fn new(value: impl Into>) -> Self { let value = value.into(); @@ -57,7 +150,7 @@ impl Noop { } impl Tlv for Noop { - const TYPE: u8 = 0x04; + const TYPE: u8 = PP2_TYPE_NOOP; fn try_from_value(value: &[u8]) -> Option { Some(Self { @@ -70,7 +163,13 @@ impl Tlv for Noop { } } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +/// 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, } @@ -79,9 +178,9 @@ impl UniqueId { /// /// /// # Panics - /// Panics if value is empty (i.e., has length of 0). - pub fn new(value: impl Into>) -> Self { - let value = value.into(); + /// 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"); @@ -90,7 +189,7 @@ impl UniqueId { } impl Tlv for UniqueId { - const TYPE: u8 = 0x05; + const TYPE: u8 = PP2_TYPE_UNIQUE_ID; fn try_from_value(value: &[u8]) -> Option { Some(Self { diff --git a/actix-proxy-protocol/src/v2.rs b/actix-proxy-protocol/src/v2.rs index 14335ade..3e3244c1 100644 --- a/actix-proxy-protocol/src/v2.rs +++ b/actix-proxy-protocol/src/v2.rs @@ -132,8 +132,12 @@ impl Header { self.tlvs.iter().any(|&(typ, _)| typ == T::TYPE) } + /// Calculates and adds a crc32c TLV to the PROXY header. + /// + /// Uses method defined in spec. + /// /// If this is not called last thing it will be wrong. - pub fn add_crc23c_checksum_tlv(&mut self) { + pub fn add_crc23c_checksum(&mut self) { // don't add a checksum if it is already set if self.has_tlv::() { return; @@ -285,7 +289,7 @@ mod tests { ); // add crc32c TLV to header - header.add_crc23c_checksum_tlv(); + header.add_crc23c_checksum(); assert_eq!(header.v2_len(), 12 + 7); assert_eq!(header.to_vec(), exp);