Skip to main content

net_io/
proxy.rs

1//! PROXY protocol vocabulary.
2//!
3//! The parser (`proxy_parse`) and the server integration are behind the `proxy`
4//! feature; the types the rest of the crate refers to exist in every build.
5
6use std::fmt;
7
8/// Whether a listener expects a PROXY protocol header.
9///
10/// There is no optional mode: the PROXY specification says a receiver must be
11/// configured for the protocol and must not guess whether a header is present.
12///
13/// # Examples
14///
15/// ```
16/// use net_io::{ProxyMode, ProxyVersions};
17/// assert_eq!(ProxyMode::default(), ProxyMode::Off);
18/// let mode = ProxyMode::Required { versions: ProxyVersions::Both };
19/// assert_ne!(mode, ProxyMode::Off);
20/// ```
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
22pub enum ProxyMode {
23    /// No header is expected.
24    #[default]
25    Off,
26    /// Every connection must start with a header from a trusted proxy.
27    Required {
28        /// Which versions are accepted.
29        versions: ProxyVersions,
30    },
31}
32
33/// Which PROXY protocol versions a listener accepts.
34///
35/// ```
36/// assert_ne!(net_io::ProxyVersions::V1, net_io::ProxyVersions::V2);
37/// ```
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum ProxyVersions {
40    /// The text version.
41    V1,
42    /// The binary version.
43    V2,
44    /// Either, chosen by the first byte.
45    Both,
46}
47
48/// Facts copied from a trusted PROXY header into fixed arrays.
49///
50/// ```
51/// let facts = net_io::ProxyFacts::default();
52/// assert!(facts.alpn().is_none());
53/// ```
54#[derive(Clone, Copy)]
55pub struct ProxyFacts {
56    alpn: [u8; 32],
57    alpn_len: u8,
58    authority: [u8; 255],
59    authority_len: u8,
60    unique_id: [u8; 128],
61    unique_id_len: u8,
62    ssl_client: Option<u8>,
63}
64
65impl Default for ProxyFacts {
66    fn default() -> ProxyFacts {
67        ProxyFacts {
68            alpn: [0; 32],
69            alpn_len: 0,
70            authority: [0; 255],
71            authority_len: 0,
72            unique_id: [0; 128],
73            unique_id_len: 0,
74            ssl_client: None,
75        }
76    }
77}
78
79impl ProxyFacts {
80    /// Copies the facts into fixed arrays; a value longer than its array is left out.
81    #[cfg_attr(not(feature = "proxy"), allow(dead_code))]
82    pub(crate) fn from_parts(
83        alpn: Option<&[u8]>,
84        authority: Option<&str>,
85        unique_id: Option<&[u8]>,
86        ssl_client: Option<u8>,
87    ) -> ProxyFacts {
88        let mut f = ProxyFacts {
89            ssl_client,
90            ..ProxyFacts::default()
91        };
92        if let Some(a) = alpn.filter(|a| a.len() <= f.alpn.len()) {
93            f.alpn[..a.len()].copy_from_slice(a);
94            f.alpn_len = a.len() as u8;
95        }
96        if let Some(a) = authority.filter(|a| a.len() <= f.authority.len()) {
97            f.authority[..a.len()].copy_from_slice(a.as_bytes());
98            f.authority_len = a.len() as u8;
99        }
100        if let Some(u) = unique_id.filter(|u| u.len() <= f.unique_id.len()) {
101            f.unique_id[..u.len()].copy_from_slice(u);
102            f.unique_id_len = u.len() as u8;
103        }
104        f
105    }
106
107    /// The ALPN protocol the proxy negotiated with the client.
108    #[must_use]
109    pub fn alpn(&self) -> Option<&[u8]> {
110        (self.alpn_len > 0).then(|| &self.alpn[..usize::from(self.alpn_len)])
111    }
112
113    /// The authority (host name) the client asked the proxy for.
114    #[must_use]
115    pub fn authority(&self) -> Option<&str> {
116        if self.authority_len == 0 {
117            return None;
118        }
119        std::str::from_utf8(&self.authority[..usize::from(self.authority_len)]).ok()
120    }
121
122    /// The proxy's unique connection id.
123    #[must_use]
124    pub fn unique_id(&self) -> Option<&[u8]> {
125        (self.unique_id_len > 0).then(|| &self.unique_id[..usize::from(self.unique_id_len)])
126    }
127
128    /// The SSL client flags the proxy reported.
129    #[must_use]
130    pub fn ssl_client(&self) -> Option<u8> {
131        self.ssl_client
132    }
133}
134
135impl fmt::Debug for ProxyFacts {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_struct("ProxyFacts")
138            .field("alpn", &self.alpn().map(String::from_utf8_lossy))
139            .field("authority", &self.authority())
140            .field("unique_id_len", &self.unique_id_len)
141            .field("ssl_client", &self.ssl_client)
142            .finish()
143    }
144}
145
146/// Why a PROXY header was refused.
147///
148/// ```
149/// assert_eq!(net_io::ProxyError::V1TooLong.to_string(), "v1 header longer than 107 bytes");
150/// ```
151#[non_exhaustive]
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub enum ProxyError {
154    /// `encode`: the output buffer cannot hold the header.
155    OutputTooSmall {
156        /// Bytes needed.
157        need: usize,
158    },
159    /// The first bytes match no allowed signature.
160    NotProxy,
161    /// A v1 line longer than 107 bytes.
162    V1TooLong,
163    /// A v1 line that does not follow the grammar.
164    V1Syntax {
165        /// Byte offset of the problem.
166        at: u8,
167    },
168    /// A v1 address that does not parse.
169    V1BadAddress,
170    /// A v1 port that does not parse.
171    V1BadPort,
172    /// A v2 version nibble other than 2.
173    V2BadVersion,
174    /// A v2 command other than LOCAL or PROXY.
175    V2BadCommand,
176    /// A v2 family and transport byte not in the specification.
177    V2BadFamily,
178    /// A v2 length above the configured maximum.
179    V2LengthTooLarge {
180        /// The declared length.
181        declared: u16,
182        /// The maximum.
183        max: u16,
184    },
185    /// A v2 address block shorter than its family needs.
186    V2AddressTooShort,
187    /// A v2 TLV that runs past the declared length.
188    V2TlvOverrun,
189    /// A v2 CRC32C TLV that does not match.
190    V2CrcMismatch,
191    /// A v2 unique id longer than 128 bytes.
192    V2UniqueIdTooLong,
193    /// A v2 SSL TLV that does not parse.
194    V2SslMalformed,
195    /// Addresses of a different family than the header declares.
196    FamilyMismatch,
197}
198
199impl fmt::Display for ProxyError {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        match self {
202            ProxyError::OutputTooSmall { need } => {
203                write!(f, "output buffer too small ({need} bytes needed)")
204            }
205            ProxyError::NotProxy => f.write_str("the connection did not start with a PROXY header"),
206            ProxyError::V1TooLong => f.write_str("v1 header longer than 107 bytes"),
207            ProxyError::V1Syntax { at } => write!(f, "v1 header syntax error at byte {at}"),
208            ProxyError::V1BadAddress => f.write_str("v1 header with an invalid address"),
209            ProxyError::V1BadPort => f.write_str("v1 header with an invalid port"),
210            ProxyError::V2BadVersion => f.write_str("v2 header with an unknown version"),
211            ProxyError::V2BadCommand => f.write_str("v2 header with an unknown command"),
212            ProxyError::V2BadFamily => f.write_str("v2 header with an unknown address family"),
213            ProxyError::V2LengthTooLarge { declared, max } => {
214                write!(
215                    f,
216                    "v2 header declares {declared} bytes, above the maximum of {max}"
217                )
218            }
219            ProxyError::V2AddressTooShort => {
220                f.write_str("v2 address block too short for its family")
221            }
222            ProxyError::V2TlvOverrun => f.write_str("v2 TLV runs past the declared length"),
223            ProxyError::V2CrcMismatch => f.write_str("v2 header checksum does not match"),
224            ProxyError::V2UniqueIdTooLong => f.write_str("v2 unique id longer than 128 bytes"),
225            ProxyError::V2SslMalformed => f.write_str("v2 SSL TLV is malformed"),
226            ProxyError::FamilyMismatch => f.write_str("addresses do not match the declared family"),
227        }
228    }
229}
230
231impl std::error::Error for ProxyError {}