Skip to main content

proxy_protocol_rs/
types.rs

1// Copyright (C) 2025-2026 Michael S. Klishin and Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::net::{IpAddr, SocketAddr};
17
18/// An address from a Proxy Protocol header
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20#[non_exhaustive]
21pub enum ProxyAddress {
22    /// IPv4 or IPv6 socket address (IP + port)
23    Inet(SocketAddr),
24    /// Unix domain socket path (up to 108 bytes per the v2 spec)
25    Unix(Vec<u8>),
26}
27
28impl ProxyAddress {
29    /// If this is an `Inet` address, return the `SocketAddr`
30    pub fn as_inet(&self) -> Option<SocketAddr> {
31        match self {
32            Self::Inet(addr) => Some(*addr),
33            _ => None,
34        }
35    }
36
37    /// If this is a `Unix` address, return the path bytes
38    pub fn as_unix(&self) -> Option<&[u8]> {
39        match self {
40            Self::Unix(path) => Some(path),
41            _ => None,
42        }
43    }
44
45    /// If this is an `Inet` address, return just the IP
46    pub fn ip(&self) -> Option<IpAddr> {
47        self.as_inet().map(|a| a.ip())
48    }
49}
50
51/// Parsed Proxy Protocol header
52#[derive(Debug, Clone, PartialEq, Eq, Hash)]
53pub struct ProxyInfo {
54    /// Protocol version (V1 or V2)
55    pub version: Version,
56
57    /// PROXY (forwarded) or LOCAL (health-check)
58    pub command: Command,
59
60    /// Transport family and protocol of the original connection;
61    /// `None` for LOCAL commands and UNKNOWN v1 lines
62    pub transport: Option<Transport>,
63
64    /// Original source (client) address; `None` for LOCAL commands
65    pub source: Option<ProxyAddress>,
66
67    /// Original destination address; `None` for LOCAL commands
68    pub destination: Option<ProxyAddress>,
69
70    /// Parsed TLV extensions (v2 only)
71    pub tlvs: Tlvs,
72}
73
74impl ProxyInfo {
75    /// Convenience: source as `SocketAddr` (IPv4/IPv6 only)
76    pub fn source_inet(&self) -> Option<SocketAddr> {
77        self.source.as_ref().and_then(ProxyAddress::as_inet)
78    }
79
80    /// Convenience: destination as `SocketAddr` (IPv4/IPv6 only)
81    pub fn destination_inet(&self) -> Option<SocketAddr> {
82        self.destination.as_ref().and_then(ProxyAddress::as_inet)
83    }
84
85    /// Convenience: source IP address, if Inet
86    pub fn source_ip(&self) -> Option<IpAddr> {
87        self.source.as_ref().and_then(ProxyAddress::ip)
88    }
89
90    /// Convenience: destination IP address, if Inet
91    pub fn destination_ip(&self) -> Option<IpAddr> {
92        self.destination.as_ref().and_then(ProxyAddress::ip)
93    }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97#[non_exhaustive]
98pub enum Version {
99    V1,
100    V2,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104#[non_exhaustive]
105pub enum Command {
106    Local,
107    Proxy,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct Transport {
112    pub family: AddressFamily,
113    pub protocol: TransportProtocol,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117#[non_exhaustive]
118pub enum AddressFamily {
119    Inet,
120    Inet6,
121    Unix,
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125#[non_exhaustive]
126pub enum TransportProtocol {
127    Stream,
128    Datagram,
129}
130
131/// Parsed TLV extensions from a v2 Proxy Protocol header
132#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
133pub struct Tlvs {
134    /// ALPN protocol negotiated (PP2_TYPE_ALPN, 0x01)
135    pub alpn: Option<Vec<u8>>,
136
137    /// SNI hostname / authority (PP2_TYPE_AUTHORITY, 0x02)
138    pub authority: Option<String>,
139
140    /// CRC32c of the entire header (PP2_TYPE_CRC32C, 0x03)
141    pub crc32c: Option<u32>,
142
143    /// Unique connection ID (PP2_TYPE_UNIQUE_ID, 0x05)
144    pub unique_id: Option<Vec<u8>>,
145
146    /// Network namespace (PP2_TYPE_NETNS, 0x30)
147    pub netns: Option<String>,
148
149    /// SSL/TLS information (PP2_TYPE_SSL, 0x20)
150    pub ssl: Option<SslInfo>,
151
152    /// All TLVs preserved as (type_byte, value), including those
153    /// already parsed into typed fields above
154    pub raw: Vec<(u8, Vec<u8>)>,
155}
156
157/// SSL/TLS metadata from the PP2_TYPE_SSL TLV (0x20)
158#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
159pub struct SslInfo {
160    /// Client connection flags
161    pub client_flags: SslClientFlags,
162
163    /// Whether the client certificate was verified
164    pub verified: bool,
165
166    /// TLS version string, e.g. "TLSv1.3"
167    pub version: Option<String>,
168
169    /// Cipher suite name
170    pub cipher: Option<String>,
171
172    /// Signature algorithm of the client certificate
173    pub sig_alg: Option<String>,
174
175    /// Key algorithm of the client certificate
176    pub key_alg: Option<String>,
177
178    /// Common Name from the client certificate's DN
179    pub cn: Option<String>,
180
181    /// TLS supported group (PP2_SUBTYPE_SSL_GROUP, 0x26)
182    pub group: Option<String>,
183
184    /// TLS signature scheme (PP2_SUBTYPE_SSL_SIG_SCHEME, 0x27)
185    pub sig_scheme: Option<String>,
186
187    /// DER-encoded client certificate (PP2_SUBTYPE_SSL_CLIENT_CERT, 0x28)
188    pub client_cert: Option<Vec<u8>>,
189}
190
191bitflags::bitflags! {
192    /// Flags from the PP2_TYPE_SSL client field
193    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
194    pub struct SslClientFlags: u8 {
195        /// Client connected over SSL/TLS
196        const SSL       = 0x01;
197        /// Client provided a certificate on this connection
198        const CERT_CONN = 0x02;
199        /// Client provided a certificate during the TLS session
200        const CERT_SESS = 0x04;
201    }
202}
203
204impl fmt::Display for ProxyAddress {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        match self {
207            Self::Inet(addr) => write!(f, "{addr}"),
208            Self::Unix(path) => match std::str::from_utf8(path) {
209                Ok(s) => write!(f, "{s}"),
210                Err(_) => write!(f, "<unix:{} bytes>", path.len()),
211            },
212        }
213    }
214}
215
216impl From<SocketAddr> for ProxyAddress {
217    fn from(addr: SocketAddr) -> Self {
218        Self::Inet(addr)
219    }
220}
221
222impl From<std::net::SocketAddrV4> for ProxyAddress {
223    fn from(addr: std::net::SocketAddrV4) -> Self {
224        Self::Inet(SocketAddr::V4(addr))
225    }
226}
227
228impl From<std::net::SocketAddrV6> for ProxyAddress {
229    fn from(addr: std::net::SocketAddrV6) -> Self {
230        Self::Inet(SocketAddr::V6(addr))
231    }
232}
233
234impl fmt::Display for Version {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        match self {
237            Self::V1 => write!(f, "v1"),
238            Self::V2 => write!(f, "v2"),
239        }
240    }
241}
242
243impl fmt::Display for Command {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        match self {
246            Self::Local => write!(f, "LOCAL"),
247            Self::Proxy => write!(f, "PROXY"),
248        }
249    }
250}
251
252impl fmt::Display for AddressFamily {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        match self {
255            Self::Inet => write!(f, "IPv4"),
256            Self::Inet6 => write!(f, "IPv6"),
257            Self::Unix => write!(f, "Unix"),
258        }
259    }
260}
261
262impl fmt::Display for TransportProtocol {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        match self {
265            Self::Stream => write!(f, "stream"),
266            Self::Datagram => write!(f, "datagram"),
267        }
268    }
269}