Skip to main content

proxy_protocol_rs/parse/
mod.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
15pub(crate) mod tlv;
16mod v1;
17mod v2;
18
19use crate::error::ParseError;
20use crate::types::ProxyInfo;
21
22/// The 12-byte v2 Proxy Protocol signature
23pub const V2_SIGNATURE: &[u8; 12] = b"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
24
25/// Parse a Proxy Protocol header (v1 or v2) from a byte slice
26///
27/// Returns the parsed `ProxyInfo` and the number of bytes consumed.
28///
29/// # Errors
30///
31/// - [`ParseError::Incomplete`] — the buffer does not yet contain a full header;
32///   the caller should read more data and retry
33/// - [`ParseError::NotProxyProtocol`] — the first bytes do not match any Proxy
34///   Protocol signature
35/// - [`ParseError::Invalid`] — the header is structurally malformed (bad version,
36///   unknown command, address family mismatch, truncated TLV, etc)
37/// - [`ParseError::CrcMismatch`] — a CRC32c TLV is present and the checksum
38///   does not match
39#[inline]
40#[must_use = "the parsed ProxyInfo contains the client address and metadata"]
41pub fn parse(buf: &[u8]) -> Result<(ProxyInfo, usize), ParseError> {
42    if buf.is_empty() {
43        return Err(ParseError::Incomplete);
44    }
45
46    match buf[0] {
47        0x0D => {
48            if buf.len() < 12 {
49                return Err(ParseError::Incomplete);
50            }
51            if buf[..12] == *V2_SIGNATURE {
52                v2::parse_v2(buf)
53            } else {
54                Err(ParseError::NotProxyProtocol)
55            }
56        }
57        b'P' => {
58            if buf.len() < 6 {
59                return Err(ParseError::Incomplete);
60            }
61            if buf.starts_with(b"PROXY ") {
62                v1::parse_v1(buf)
63            } else {
64                Err(ParseError::NotProxyProtocol)
65            }
66        }
67        _ => Err(ParseError::NotProxyProtocol),
68    }
69}