actix_proxy_protocol/
tlv.rs

1use std::{borrow::Cow, convert::TryFrom, str};
2
3const PP2_TYPE_ALPN: u8 = 0x01; //           done
4const PP2_TYPE_AUTHORITY: u8 = 0x02; //      done
5const PP2_TYPE_CRC32C: u8 = 0x03; //         done
6const PP2_TYPE_NOOP: u8 = 0x04; //           done
7const PP2_TYPE_UNIQUE_ID: u8 = 0x05; //      done
8const PP2_TYPE_SSL: u8 = 0x20;
9const PP2_SUBTYPE_SSL_VERSION: u8 = 0x21;
10const PP2_SUBTYPE_SSL_CN: u8 = 0x22;
11const PP2_SUBTYPE_SSL_CIPHER: u8 = 0x23;
12const PP2_SUBTYPE_SSL_SIG_ALG: u8 = 0x24;
13const PP2_SUBTYPE_SSL_KEY_ALG: u8 = 0x25;
14const PP2_TYPE_NETNS: u8 = 0x30;
15
16pub trait Tlv: Sized {
17    const TYPE: u8;
18
19    fn try_from_value(value: &[u8]) -> Option<Self>;
20
21    fn value_bytes(&self) -> Cow<'_, [u8]>;
22
23    fn try_from_parts(typ: u8, value: &[u8]) -> Option<Self> {
24        if typ != Self::TYPE {
25            return None;
26        }
27
28        Self::try_from_value(value)
29    }
30}
31
32/// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence defining
33/// the upper layer protocol in use over the connection. The most common use case
34/// will be to pass the exact copy of the ALPN extension of the Transport Layer
35/// Security (TLS) protocol as defined by RFC7301 [9].
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Alpn {
38    alpn: Vec<u8>,
39}
40
41impl Alpn {
42    ///
43    ///
44    /// # Panics
45    /// Panics if `alpn` is empty (i.e., has length of 0).
46    pub fn new(alpn: impl Into<Vec<u8>>) -> Self {
47        let alpn = alpn.into();
48
49        assert!(!alpn.is_empty(), "ALPN TLV value cannot be empty");
50
51        Self { alpn }
52    }
53}
54
55impl Tlv for Alpn {
56    const TYPE: u8 = PP2_TYPE_ALPN;
57
58    fn try_from_value(value: &[u8]) -> Option<Self> {
59        Some(Self {
60            alpn: value.to_owned(),
61        })
62    }
63
64    fn value_bytes(&self) -> Cow<'_, [u8]> {
65        Cow::Borrowed(&self.alpn)
66    }
67}
68
69/// Contains the host name value passed by the client, as an UTF8-encoded string.
70/// In case of TLS being used on the client connection, this is the exact copy of
71/// the "server_name" extension as defined by RFC3546 [10], section 3.1, often
72/// referred to as "SNI". There are probably other situations where an authority
73/// can be mentioned on a connection without TLS being involved at all.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct Authority {
76    authority: String,
77}
78
79impl Authority {
80    /// A UTF-8
81    ///
82    /// # Panics
83    /// Panics if `authority` is an empty string.
84    pub fn new(authority: impl Into<String>) -> Self {
85        let authority = authority.into();
86
87        assert!(!authority.is_empty(), "Authority TLV value cannot be empty");
88
89        Self { authority }
90    }
91}
92
93impl Tlv for Authority {
94    const TYPE: u8 = PP2_TYPE_AUTHORITY;
95
96    fn try_from_value(value: &[u8]) -> Option<Self> {
97        Some(Self {
98            authority: str::from_utf8(value).ok()?.to_owned(),
99        })
100    }
101
102    fn value_bytes(&self) -> Cow<'_, [u8]> {
103        Cow::Borrowed(self.authority.as_bytes())
104    }
105}
106
107/// The value of the type PP2_TYPE_CRC32C is a 32-bit number storing the CRC32c
108/// checksum of the PROXY protocol header.
109#[derive(Debug, Clone, Default, PartialEq, Eq)]
110pub struct Crc32c {
111    pub(crate) checksum: u32,
112}
113
114impl Tlv for Crc32c {
115    const TYPE: u8 = PP2_TYPE_CRC32C;
116
117    fn try_from_value(value: &[u8]) -> Option<Self> {
118        let checksum_bytes = <[u8; 4]>::try_from(value).ok()?;
119
120        Some(Self {
121            checksum: u32::from_be_bytes(checksum_bytes),
122        })
123    }
124
125    fn value_bytes(&self) -> Cow<'_, [u8]> {
126        Cow::Owned(self.checksum.to_be_bytes().to_vec())
127    }
128}
129
130/// The TLV of this type should be ignored when parsed. The value is zero or more
131/// bytes. Can be used for data padding or alignment. Note that it can be used
132/// to align only by 3 or more bytes because a TLV can not be smaller than that.
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct Noop {
135    value: Vec<u8>,
136}
137
138impl Noop {
139    ///
140    ///
141    /// # Panics
142    /// Panics if `value` is empty (i.e., has length of 0).
143    pub fn new(value: impl Into<Vec<u8>>) -> Self {
144        let value = value.into();
145
146        assert!(!value.is_empty(), "Noop TLV `value` cannot be empty");
147
148        Self { value }
149    }
150}
151
152impl Tlv for Noop {
153    const TYPE: u8 = PP2_TYPE_NOOP;
154
155    fn try_from_value(value: &[u8]) -> Option<Self> {
156        Some(Self {
157            value: value.to_owned(),
158        })
159    }
160
161    fn value_bytes(&self) -> Cow<'_, [u8]> {
162        Cow::Borrowed(&self.value)
163    }
164}
165
166/// The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to
167/// 128 bytes generated by the upstream proxy that uniquely identifies the
168/// connection.
169///
170/// The unique ID can be used to easily correlate connections across multiple
171/// layers of proxies, without needing to look up IP addresses and port numbers.
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct UniqueId {
174    value: Vec<u8>,
175}
176
177impl UniqueId {
178    ///
179    ///
180    /// # Panics
181    /// Panics if `value` is 0 bytes or larger than 128 bytes.
182    pub fn new(id: impl Into<Vec<u8>>) -> Self {
183        let value = id.into();
184
185        assert!(!value.is_empty(), "UniqueId TLV `value` cannot be empty");
186        assert!(
187            value.len() < 128,
188            "UniqueId TLV `value` cannot be larger than 128 bytes"
189        );
190
191        Self { value }
192    }
193}
194
195impl Tlv for UniqueId {
196    const TYPE: u8 = PP2_TYPE_UNIQUE_ID;
197
198    fn try_from_value(value: &[u8]) -> Option<Self> {
199        Some(Self {
200            value: value.to_owned(),
201        })
202    }
203
204    fn value_bytes(&self) -> Cow<'_, [u8]> {
205        Cow::Borrowed(&self.value)
206    }
207}
208
209bitflags::bitflags! {
210    #[derive(Debug, Clone, PartialEq, Eq)]
211    struct SslClientFlags: u8 {
212        const PP2_CLIENT_SSL = 0x01;
213        const PP2_CLIENT_CERT_CONN = 0x02;
214        const PP2_CLIENT_CERT_SESS = 0x04;
215    }
216}
217
218/// TLS (SSL).
219///
220/// Very broken atm.
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct Ssl {
223    /// The <client> field is made of a bit field indicating which element is present.
224    ///
225    /// Note, that each of these elements may lead to extra data being appended to
226    /// this TLV using a second level of TLV encapsulation. It is thus possible to
227    /// find multiple TLV values after this field. The total length of the pp2_tlv_ssl
228    /// TLV will reflect this.
229    client: SslClientFlags,
230
231    /// The <verify> field will be zero if the client presented a certificate
232    /// and it was successfully verified, and non-zero otherwise.
233    verify: bool,
234
235    /// Sub-TLVs.
236    tlvs: Vec<SslTlv>,
237}
238
239impl Tlv for Ssl {
240    const TYPE: u8 = PP2_TYPE_SSL;
241
242    fn try_from_value(_value: &[u8]) -> Option<Self> {
243        /// The PP2_CLIENT_SSL flag indicates that the client connected over SSL/TLS. When
244        /// this field is present, the US-ASCII string representation of the TLS version is
245        /// appended at the end of the field in the TLV format using the type
246        /// PP2_SUBTYPE_SSL_VERSION.
247        const PP2_CLIENT_SSL: u8 = 0x01;
248
249        /// PP2_CLIENT_CERT_CONN indicates that the client provided a certificate over the
250        /// current connection.
251        const PP2_CLIENT_CERT_CONN: u8 = 0x02;
252
253        /// PP2_CLIENT_CERT_SESS indicates that the client provided a
254        /// certificate at least once over the TLS session this connection belongs to.
255        const PP2_CLIENT_CERT_SESS: u8 = 0x04;
256
257        // TODO: finish parsing
258
259        None
260    }
261
262    fn value_bytes(&self) -> Cow<'_, [u8]> {
263        Cow::Borrowed(&[])
264    }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
268struct SslTlv {}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    // #[test]
275    // #[should_panic]
276    // fn tlv_zero_len() {
277    //     Tlv::new(0x00, vec![]);
278    // }
279
280    #[test]
281    fn tlv_as_crc32c() {
282        // noop
283        assert_eq!(Crc32c::try_from_parts(0x04, &[0x00]), None);
284
285        assert_eq!(
286            Crc32c::try_from_parts(0x03, &[0x08, 0x70, 0x17, 0x7b]),
287            Some(Crc32c {
288                checksum: 141563771
289            })
290        );
291    }
292}