proxy_protocol_codec/v2/model.rs
1//! PROXY Protocol v2 header models
2
3#[cfg(feature = "feat-codec-encode")]
4use alloc::vec::Vec;
5use core::net::{Ipv4Addr, Ipv6Addr};
6#[cfg(feature = "feat-codec-v2-uni-addr")]
7use std::io;
8
9#[cfg(feature = "feat-codec-decode")]
10use slicur::Reader;
11
12#[cfg(feature = "feat-codec-decode")]
13use crate::v2::DecodeError;
14
15#[cfg(any(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
16/// Fixed version byte for PROXY Protocol v2.
17pub(crate) const BYTE_VERSION: u8 = 0x20;
18
19/// Size of the PROXY Protocol v2 header in bytes.
20pub const HEADER_SIZE: usize = 16;
21
22#[cfg(any(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
23/// Size of addresses for IPv4
24pub(crate) const ADDR_INET_SIZE: usize = 12; // 2 * 4 bytes for IPv4 + 2 * 2 bytes for port
25
26#[cfg(any(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
27/// Size of addresses for IPv6
28pub(crate) const ADDR_INET6_SIZE: usize = 36; // 2 * 16 bytes for IPv6 + 2 * 2 bytes for port
29
30#[cfg(any(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
31/// Size of addresses for Unix sockets
32pub(crate) const ADDR_UNIX_SIZE: usize = 216; // 2 * 108 bytes for Unix socket addresses
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[repr(u8)]
36/// The supported `Command`s for a PROXY protocol header.
37pub enum Command {
38 /// The connection was established on purpose by the proxy
39 /// without being relayed. The connection endpoints are the sender and the
40 /// receiver. Such connections exist when the proxy sends health-checks to
41 /// the server. The receiver must accept this connection as valid and
42 /// must use the real connection endpoints and discard the protocol
43 /// block including the family which is ignored.
44 Local = 0x00,
45
46 /// the connection was established on behalf of another node, and reflects
47 /// the original connection endpoints. The receiver must then use the
48 /// information provided in the protocol block to get original the address.
49 Proxy = 0x01,
50}
51
52#[cfg(any(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[repr(u8)]
55/// The address family.
56pub(crate) enum Family {
57 /// The connection is forwarded for an unknown, unspecified or unsupported
58 /// protocol. The sender should use this family when sending LOCAL commands
59 /// or when dealing with unsupported protocol families. The receiver is free
60 /// to accept the connection anyway and use the real endpoint addresses or
61 /// to reject it. The receiver should ignore address information.
62 Unspecified = 0x00,
63
64 /// The forwarded connection uses the `AF_INET` address family (IPv4). The
65 /// addresses are exactly 4 bytes each in network byte order, followed by
66 /// transport protocol information (typically ports).
67 Inet = 0x10,
68
69 /// The forwarded connection uses the `AF_INET6` address family (IPv6). The
70 /// addresses are exactly 16 bytes each in network byte order, followed by
71 /// transport protocol information (typically ports).
72 Inet6 = 0x20,
73
74 /// The forwarded connection uses the `AF_UNIX` address family (UNIX). The
75 /// addresses are exactly 108 bytes each.
76 Unix = 0x30,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[repr(u8)]
81/// The transport protocol.
82pub enum Protocol {
83 /// The connection is forwarded for an unknown, unspecified or unsupported
84 /// protocol. The sender should use this protocol when sending LOCAL
85 /// commands or when dealing with unsupported protocols. The receiver is
86 /// free to accept the connection anyway and use the real endpoint
87 /// addresses or to reject it.
88 Unspecified = 0x00,
89
90 /// The forwarded connection uses a `SOCK_STREAM` protocol (eg: TCP or
91 /// `UNIX_STREAM`). When used with `AF_INET/AF_INET6` (TCP), the addresses
92 /// are followed by the source and destination ports represented on 2
93 /// bytes each in network byte order.
94 Stream = 0x01,
95
96 /// The forwarded connection uses a `SOCK_DGRAM` protocol (eg: UDP or
97 /// `UNIX_DGRAM`). When used with `AF_INET/AF_INET6` (UDP), the addresses
98 /// are followed by the source and destination ports represented on 2
99 /// bytes each in network byte order.
100 Dgram = 0x02,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104/// The address type, which can be either an IPv4/IPv6 address or a UNIX socket
105/// address.
106pub enum AddressPair {
107 /// Address unspecified
108 Unspecified,
109
110 /// The address is an IPv4 address.
111 Inet {
112 /// SRC IPv4 address.
113 src_ip: Ipv4Addr,
114
115 /// DST IPv4 address.
116 dst_ip: Ipv4Addr,
117
118 /// SRC port.
119 src_port: u16,
120
121 /// DST port.
122 dst_port: u16,
123 },
124
125 /// The address is an IPv6 address.
126 Inet6 {
127 /// SRC IPv4 address.
128 src_ip: Ipv6Addr,
129
130 /// DST IPv4 address.
131 dst_ip: Ipv6Addr,
132
133 /// SRC port.
134 src_port: u16,
135
136 /// DST port.
137 dst_port: u16,
138 },
139
140 /// The address is a UNIX socket address.
141 Unix {
142 /// The src address bytes (with null terminator).
143 src_addr: [u8; 108],
144
145 /// The address bytes (with null terminator).
146 dst_addr: [u8; 108],
147 },
148}
149
150impl AddressPair {
151 #[cfg(feature = "feat-codec-encode")]
152 #[inline]
153 pub(crate) const fn address_family(&self) -> Family {
154 match self {
155 Self::Unspecified => Family::Unspecified,
156 Self::Inet { .. } => Family::Inet,
157 Self::Inet6 { .. } => Family::Inet6,
158 Self::Unix { .. } => Family::Unix,
159 }
160 }
161
162 #[cfg(feature = "feat-codec-v2-uni-addr")]
163 /// Returns the source address.
164 pub fn src_uni_addr(&self) -> io::Result<Option<uni_addr::SocketAddr>> {
165 use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
166
167 match self {
168 Self::Unspecified => Ok(None),
169 Self::Inet { src_ip, src_port, .. } => Ok(Some(uni_addr::SocketAddr::Inet(SocketAddr::V4(
170 SocketAddrV4::new(*src_ip, *src_port),
171 )))),
172 Self::Inet6 { src_ip, src_port, .. } => Ok(Some(uni_addr::SocketAddr::Inet(SocketAddr::V6(
173 SocketAddrV6::new(*src_ip, *src_port, 0, 0),
174 )))),
175 #[cfg(unix)]
176 Self::Unix { src_addr, .. } => uni_addr::unix::SocketAddr::from_bytes_until_nul(src_addr)
177 .map(uni_addr::SocketAddr::Unix)
178 .map(Some),
179 #[cfg(not(unix))]
180 Self::Unix { .. } => Err(io::Error::new(
181 io::ErrorKind::Unsupported,
182 "Unix socket addresses are not supported on this platform",
183 )),
184 }
185 }
186
187 #[cfg(feature = "feat-codec-v2-uni-addr")]
188 /// Returns the destination address.
189 pub fn dst_uni_addr(&self) -> io::Result<Option<uni_addr::SocketAddr>> {
190 use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
191
192 match self {
193 Self::Unspecified => Ok(None),
194 Self::Inet { dst_ip, dst_port, .. } => Ok(Some(uni_addr::SocketAddr::Inet(SocketAddr::V4(
195 SocketAddrV4::new(*dst_ip, *dst_port),
196 )))),
197 Self::Inet6 { dst_ip, dst_port, .. } => Ok(Some(uni_addr::SocketAddr::Inet(SocketAddr::V6(
198 SocketAddrV6::new(*dst_ip, *dst_port, 0, 0),
199 )))),
200 #[cfg(unix)]
201 Self::Unix { dst_addr, .. } => uni_addr::unix::SocketAddr::from_bytes_until_nul(dst_addr)
202 .map(uni_addr::SocketAddr::Unix)
203 .map(Some),
204 #[cfg(not(unix))]
205 Self::Unix { .. } => Err(io::Error::new(
206 io::ErrorKind::Unsupported,
207 "Unix socket addresses are not supported on this platform",
208 )),
209 }
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214#[repr(u8)]
215/// Supported types for `TypeLengthValue` payloads.
216pub enum ExtensionType {
217 /// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence
218 /// defining the upper layer protocol in use over the connection. The most
219 /// common use case will be to pass the exact copy of the ALPN extension of
220 /// the Transport Layer Security (TLS) protocol as defined by RFC7301.
221 ALPN = 0x01,
222
223 /// Contains the host name value passed by the client, as an UTF8-encoded
224 /// string. In case of TLS being used on the client connection, this is the
225 /// exact copy of the "`server_name`" extension as defined by RFC3546,
226 /// section 3.1, often referred to as "SNI". There are probably other
227 /// situations where an authority can be mentioned on a connection without
228 /// TLS being involved at all.
229 Authority = 0x02,
230
231 /// The value of the type `PP2_TYPE_CRC32C` is a 32-bit number storing the
232 /// `CRC32c` checksum of the PROXY protocol header.
233 ///
234 /// When the checksum is supported by the sender after constructing the
235 /// header the sender MUST:
236 ///
237 /// - Initialize the checksum field to '0's.
238 /// - Calculate the `CRC32c` checksum of the PROXY header as described in
239 /// RFC4960, Appendix B.
240 /// - Put the resultant value into the checksum field, and leave the rest
241 /// of the bits unchanged.
242 ///
243 /// If the checksum is provided as part of the PROXY header and the checksum
244 /// functionality is supported by the receiver, the receiver MUST:
245 ///
246 /// - Store the received `CRC32c` checksum value aside.
247 /// - Replace the 32 bits of the checksum field in the received PROXY
248 /// header with all '0's and calculate a `CRC32c` checksum value of the
249 /// whole PROXY header.
250 /// - Verify that the calculated `CRC32c` checksum is the same as the
251 /// received `CRC32c` checksum. If it is not, the receiver MUST treat the
252 /// TCP connection providing the header as invalid.
253 ///
254 /// The default procedure for handling an invalid TCP connection is to abort
255 /// it.
256 CRC32C = 0x03,
257
258 /// The TLV of this type should be ignored when parsed. The value is zero or
259 /// more bytes. Can be used for data padding or alignment. Note that it can
260 /// be used to align only by 3 or more bytes because a TLV can not be
261 /// smaller than that.
262 NoOp = 0x04,
263
264 /// The value of the type `PP2_TYPE_UNIQUE_ID` is an opaque byte sequence of
265 /// up to 128 bytes generated by the upstream proxy that uniquely identifies
266 /// the connection.
267 ///
268 /// The unique ID can be used to easily correlate connections across
269 /// multiple layers of proxies, without needing to look up IP addresses and
270 /// port numbers.
271 UniqueId = 0x05,
272
273 /// The type `PP2_TYPE_NETNS` defines the value as the US-ASCII string
274 /// representation of the namespace's name.
275 NetworkNamespace = 0x30,
276}
277
278impl ExtensionType {
279 #[inline]
280 const fn from_u8(value: u8) -> Option<Self> {
281 Some(match value {
282 v if v == Self::ALPN as u8 => Self::ALPN,
283 v if v == Self::Authority as u8 => Self::Authority,
284 v if v == Self::CRC32C as u8 => Self::CRC32C,
285 v if v == Self::NoOp as u8 => Self::NoOp,
286 v if v == Self::UniqueId as u8 => Self::UniqueId,
287 v if v == Self::NetworkNamespace as u8 => Self::NetworkNamespace,
288 _ => return None,
289 })
290 }
291}
292
293/// A type-length-value (TLV) extension in the PROXY Protocol v2 header.
294#[derive(Debug, Clone, Copy)]
295pub struct ExtensionRef<'a> {
296 /// The type of the extension.
297 typ: u8,
298
299 #[allow(unused)]
300 /// The length of the value in bytes.
301 len: u16,
302
303 /// The value of the extension.
304 payload: &'a [u8],
305}
306
307impl<'a> ExtensionRef<'a> {
308 #[inline]
309 /// Creates a new `ExtensionRef` from the given given type and payload.
310 ///
311 /// If the length of the payload exceeds `u16::MAX`, returns `None`.
312 pub const fn new(typ: ExtensionType, payload: &'a [u8]) -> Option<Self> {
313 Self::new_custom(typ as u8, payload)
314 }
315
316 #[inline]
317 /// Creates a new `ExtensionRef` from the given custom type and payload.
318 pub const fn new_custom(typ: u8, payload: &'a [u8]) -> Option<Self> {
319 let len = payload.len();
320
321 if len > u16::MAX as usize {
322 return None; // Length exceeds maximum allowed size
323 }
324
325 Some(Self {
326 typ,
327 len: len as u16,
328 payload,
329 })
330 }
331
332 #[inline]
333 /// Returns the type of the extension.
334 ///
335 /// If the type is not recognized, returns an `Err` with the raw type byte.
336 pub const fn typ(&self) -> Result<ExtensionType, u8> {
337 match ExtensionType::from_u8(self.typ) {
338 Some(typ) => Ok(typ),
339 None => Err(self.typ),
340 }
341 }
342
343 #[inline]
344 /// Returns the payload of the extension.
345 pub const fn payload(&self) -> &'a [u8] {
346 self.payload
347 }
348
349 #[inline]
350 #[cfg(feature = "feat-codec-encode")]
351 pub(crate) fn encode(&self, buf: &mut Vec<u8>) {
352 buf.reserve(self.len as usize);
353 buf.push(self.typ);
354 buf.extend(&self.len.to_be_bytes());
355 buf.extend(self.payload);
356 }
357
358 #[inline]
359 #[cfg(feature = "feat-codec-decode")]
360 /// Decodes a single "type-length-value" extension from the provided reader.
361 ///
362 /// # Safety
363 ///
364 /// The caller must validate the header's total length before calling this
365 /// method. Returns `Err(())` if the header is malformed or corrupted.
366 pub(crate) fn decode(reader: &mut Reader<'a>) -> Result<Option<Self>, DecodeError> {
367 let Ok(typ) = reader.read_u8() else {
368 // No more extensions to read
369 return Ok(None);
370 };
371 let Ok(len) = reader.read_u16() else {
372 return Err(DecodeError::MalformedData);
373 };
374 let Ok(payload) = reader.take(len as usize) else {
375 return Err(DecodeError::MalformedData);
376 };
377
378 Ok(Some(Self { typ, len, payload }))
379 }
380}