Skip to main content

net_io/
proxy.rs

1//! PROXY protocol vocabulary.
2//!
3//! The parser and the server integration arrive in `0.4.0` 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    /// The ALPN protocol the proxy negotiated with the client.
81    #[must_use]
82    pub fn alpn(&self) -> Option<&[u8]> {
83        (self.alpn_len > 0).then(|| &self.alpn[..usize::from(self.alpn_len)])
84    }
85
86    /// The authority (host name) the client asked the proxy for.
87    #[must_use]
88    pub fn authority(&self) -> Option<&str> {
89        if self.authority_len == 0 {
90            return None;
91        }
92        std::str::from_utf8(&self.authority[..usize::from(self.authority_len)]).ok()
93    }
94
95    /// The proxy's unique connection id.
96    #[must_use]
97    pub fn unique_id(&self) -> Option<&[u8]> {
98        (self.unique_id_len > 0).then(|| &self.unique_id[..usize::from(self.unique_id_len)])
99    }
100
101    /// The SSL client flags the proxy reported.
102    #[must_use]
103    pub fn ssl_client(&self) -> Option<u8> {
104        self.ssl_client
105    }
106}
107
108impl fmt::Debug for ProxyFacts {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("ProxyFacts")
111            .field("alpn", &self.alpn().map(String::from_utf8_lossy))
112            .field("authority", &self.authority())
113            .field("unique_id_len", &self.unique_id_len)
114            .field("ssl_client", &self.ssl_client)
115            .finish()
116    }
117}
118
119/// Why a PROXY header was refused.
120///
121/// ```
122/// assert_eq!(net_io::ProxyError::V1TooLong.to_string(), "v1 header longer than 107 bytes");
123/// ```
124#[non_exhaustive]
125#[derive(Clone, Copy, Debug, PartialEq, Eq)]
126pub enum ProxyError {
127    /// `encode`: the output buffer cannot hold the header.
128    OutputTooSmall {
129        /// Bytes needed.
130        need: usize,
131    },
132    /// The first bytes match no allowed signature.
133    NotProxy,
134    /// A v1 line longer than 107 bytes.
135    V1TooLong,
136    /// A v1 line that does not follow the grammar.
137    V1Syntax {
138        /// Byte offset of the problem.
139        at: u8,
140    },
141    /// A v1 address that does not parse.
142    V1BadAddress,
143    /// A v1 port that does not parse.
144    V1BadPort,
145    /// A v2 version nibble other than 2.
146    V2BadVersion,
147    /// A v2 command other than LOCAL or PROXY.
148    V2BadCommand,
149    /// A v2 family and transport byte not in the specification.
150    V2BadFamily,
151    /// A v2 length above the configured maximum.
152    V2LengthTooLarge {
153        /// The declared length.
154        declared: u16,
155        /// The maximum.
156        max: u16,
157    },
158    /// A v2 address block shorter than its family needs.
159    V2AddressTooShort,
160    /// A v2 TLV that runs past the declared length.
161    V2TlvOverrun,
162    /// A v2 CRC32C TLV that does not match.
163    V2CrcMismatch,
164    /// A v2 unique id longer than 128 bytes.
165    V2UniqueIdTooLong,
166    /// A v2 SSL TLV that does not parse.
167    V2SslMalformed,
168    /// Addresses of a different family than the header declares.
169    FamilyMismatch,
170}
171
172impl fmt::Display for ProxyError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            ProxyError::OutputTooSmall { need } => {
176                write!(f, "output buffer too small ({need} bytes needed)")
177            }
178            ProxyError::NotProxy => f.write_str("the connection did not start with a PROXY header"),
179            ProxyError::V1TooLong => f.write_str("v1 header longer than 107 bytes"),
180            ProxyError::V1Syntax { at } => write!(f, "v1 header syntax error at byte {at}"),
181            ProxyError::V1BadAddress => f.write_str("v1 header with an invalid address"),
182            ProxyError::V1BadPort => f.write_str("v1 header with an invalid port"),
183            ProxyError::V2BadVersion => f.write_str("v2 header with an unknown version"),
184            ProxyError::V2BadCommand => f.write_str("v2 header with an unknown command"),
185            ProxyError::V2BadFamily => f.write_str("v2 header with an unknown address family"),
186            ProxyError::V2LengthTooLarge { declared, max } => {
187                write!(
188                    f,
189                    "v2 header declares {declared} bytes, above the maximum of {max}"
190                )
191            }
192            ProxyError::V2AddressTooShort => {
193                f.write_str("v2 address block too short for its family")
194            }
195            ProxyError::V2TlvOverrun => f.write_str("v2 TLV runs past the declared length"),
196            ProxyError::V2CrcMismatch => f.write_str("v2 header checksum does not match"),
197            ProxyError::V2UniqueIdTooLong => f.write_str("v2 unique id longer than 128 bytes"),
198            ProxyError::V2SslMalformed => f.write_str("v2 SSL TLV is malformed"),
199            ProxyError::FamilyMismatch => f.write_str("addresses do not match the declared family"),
200        }
201    }
202}
203
204impl std::error::Error for ProxyError {}