actix-net/actix-proxy-protocol/src/tlv.rs

228 lines
5.8 KiB
Rust

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<Self>;
fn value_bytes(&self) -> Cow<'_, [u8]>;
fn try_from_parts(typ: u8, value: &[u8]) -> Option<Self> {
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<u8>,
}
impl Alpn {
///
///
/// # Panics
/// Panics if `alpn` is empty (i.e., has length of 0).
pub fn new(alpn: impl Into<Vec<u8>>) -> 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<Self> {
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<String>) -> 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<Self> {
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<Self> {
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<u8>,
}
impl Noop {
///
///
/// # Panics
/// Panics if `value` is empty (i.e., has length of 0).
pub fn new(value: impl Into<Vec<u8>>) -> 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<Self> {
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<u8>,
}
impl UniqueId {
///
///
/// # Panics
/// Panics if `value` is empty (i.e., has length of 0).
pub fn new(id: impl Into<Vec<u8>>) -> 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<Self> {
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
})
);
}
}