Skip to main content

net_io/
proxy_parse.rs

1//! The PROXY protocol parser (versions 1 and 2): a sans-IO state machine.
2//!
3//! Give [`ProxyParser::feed`] every byte received so far; it resumes where it
4//! stopped and never reads past the end of the header, so the bytes after
5//! `consumed` stay application data. The grammar follows the HAProxy PROXY
6//! protocol specification; the v1 text is parsed strictly (single spaces, no
7//! leading zeros, addresses parsed by this module's own grammar).
8
9use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
10use std::str;
11
12use crate::crc32c::Crc32c;
13use crate::proxy::{ProxyError, ProxyFacts, ProxyVersions};
14
15const V1_PREFIX: &[u8] = b"PROXY ";
16const V1_MAX: usize = 107;
17const V2_SIGNATURE: [u8; 12] = [
18    0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,
19];
20const V2_FIXED: usize = 16;
21
22const TLV_ALPN: u8 = 0x01;
23const TLV_AUTHORITY: u8 = 0x02;
24const TLV_CRC32C: u8 = 0x03;
25const TLV_NOOP: u8 = 0x04;
26const TLV_UNIQUE_ID: u8 = 0x05;
27const TLV_SSL: u8 = 0x20;
28const SSL_VERSION: u8 = 0x21;
29const SSL_CN: u8 = 0x22;
30const SSL_CIPHER: u8 = 0x23;
31const SSL_SIG_ALG: u8 = 0x24;
32const SSL_KEY_ALG: u8 = 0x25;
33const TLV_NETNS: u8 = 0x30;
34const UNIQUE_ID_MAX: usize = 128;
35
36/// Which version the parser is reading.
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38enum Kind {
39    Undecided,
40    V1,
41    V2,
42}
43
44/// A sans-IO PROXY header parser for one connection.
45///
46/// # Examples
47///
48/// ```
49/// use net_io::{ProxyParser, ProxyProgress, ProxyVersions};
50/// let mut parser = ProxyParser::new(ProxyVersions::Both, 1024);
51/// let bytes = b"PROXY TCP4 192.0.2.1 198.51.100.2 51000 443\r\nGET / HTTP/1.1\r\n";
52/// match parser.feed(bytes)? {
53///     ProxyProgress::Done { header, consumed } => {
54///         assert_eq!(&bytes[consumed..], b"GET / HTTP/1.1\r\n");
55///         assert_eq!(header.source.unwrap().to_string(), "192.0.2.1:51000");
56///     }
57///     ProxyProgress::NeedMore { .. } => unreachable!(),
58/// }
59/// # Ok::<(), net_io::ProxyError>(())
60/// ```
61#[derive(Clone, Debug)]
62pub struct ProxyParser {
63    versions: ProxyVersions,
64    v2_body_max: u16,
65    kind: Kind,
66    /// v1: bytes already searched for the line end.
67    scanned: usize,
68}
69
70/// What [`ProxyParser::feed`] found.
71#[derive(Debug)]
72pub enum ProxyProgress<'a> {
73    /// The header is incomplete: feed again with at least this many more bytes.
74    NeedMore {
75        /// The fewest additional bytes that can make progress.
76        at_least: usize,
77    },
78    /// A complete header; the bytes from `consumed` on are application data.
79    Done {
80        /// The header, borrowing from the input.
81        header: ProxyHeader<'a>,
82        /// The header's length in bytes.
83        consumed: usize,
84    },
85}
86
87/// A parsed PROXY header, borrowing from the bytes it was parsed from.
88#[non_exhaustive]
89#[derive(Clone, Copy, Debug)]
90pub struct ProxyHeader<'a> {
91    /// 1 or 2.
92    pub version: u8,
93    /// `Proxy`, or `Local` (a connection made by the proxy itself, such as a
94    /// health check: keep the socket's own addresses).
95    pub command: ProxyCommand,
96    /// The transport of the proxied connection.
97    pub transport: ProxyTransport,
98    /// The real client; `None` for `LOCAL`, v1 `UNKNOWN`, and v2 `UNSPEC`.
99    pub source: Option<ProxyAddress<'a>>,
100    /// The address the client connected to; `None` as for `source`.
101    pub destination: Option<ProxyAddress<'a>>,
102    /// The v2 TLVs (empty for v1); `NOOP` entries are skipped.
103    pub tlvs: Tlvs<'a>,
104    crc_checked: bool,
105}
106
107/// The PROXY command.
108#[non_exhaustive]
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
110pub enum ProxyCommand {
111    /// A proxied connection.
112    Proxy,
113    /// A connection made by the proxy itself.
114    Local,
115}
116
117/// The transport of the proxied connection.
118#[non_exhaustive]
119#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub enum ProxyTransport {
121    /// A stream (TCP, or a Unix stream socket).
122    Stream,
123    /// Datagrams.
124    Datagram,
125    /// Not given.
126    Unspecified,
127}
128
129/// An address in a PROXY header.
130#[non_exhaustive]
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132pub enum ProxyAddress<'a> {
133    /// An IP address and port.
134    Ip(SocketAddr),
135    /// A Unix socket path (v2 only), with trailing zero bytes removed.
136    Unix(&'a [u8]),
137}
138
139impl ProxyAddress<'_> {
140    /// The IP socket address, if this is one.
141    #[must_use]
142    pub fn socket_addr(&self) -> Option<SocketAddr> {
143        match self {
144            ProxyAddress::Ip(a) => Some(*a),
145            ProxyAddress::Unix(_) => None,
146        }
147    }
148}
149
150impl std::fmt::Display for ProxyAddress<'_> {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        match self {
153            ProxyAddress::Ip(a) => write!(f, "{a}"),
154            ProxyAddress::Unix(p) => write!(f, "{}", String::from_utf8_lossy(p)),
155        }
156    }
157}
158
159/// One v2 TLV.
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
161pub struct Tlv<'a> {
162    /// The type byte.
163    pub kind: u8,
164    /// The value.
165    pub value: &'a [u8],
166}
167
168/// The TLVs of a v2 header, already checked to fit the declared length.
169#[derive(Clone, Copy, Debug)]
170pub struct Tlvs<'a> {
171    rest: &'a [u8],
172}
173
174impl<'a> Iterator for Tlvs<'a> {
175    type Item = Tlv<'a>;
176
177    fn next(&mut self) -> Option<Tlv<'a>> {
178        loop {
179            let (tlv, rest) = split_tlv(self.rest)?;
180            self.rest = rest;
181            if tlv.kind != TLV_NOOP {
182                return Some(tlv);
183            }
184        }
185    }
186}
187
188/// The SSL facts a proxy that terminated TLS reports (v2 TLV `0x20`).
189#[non_exhaustive]
190#[derive(Clone, Copy, Debug)]
191pub struct ProxySsl<'a> {
192    /// The client flags: bit 0 SSL, bit 1 a client certificate on this
193    /// connection, bit 2 one in the session.
194    pub client: u8,
195    /// Whether the client certificate was verified (`verify` field 0).
196    pub verified: bool,
197    /// The TLS version, such as `TLSv1.3`.
198    pub version: Option<&'a str>,
199    /// The client certificate's common name.
200    pub common_name: Option<&'a str>,
201    /// The cipher.
202    pub cipher: Option<&'a str>,
203    /// The certificate's signature algorithm.
204    pub signature_algorithm: Option<&'a str>,
205    /// The certificate's key algorithm.
206    pub key_algorithm: Option<&'a str>,
207}
208
209impl<'a> ProxyHeader<'a> {
210    fn find(&self, kind: u8) -> Option<&'a [u8]> {
211        let mut tlvs = self.tlvs;
212        tlvs.find(|t| t.kind == kind).map(|t| t.value)
213    }
214
215    /// The ALPN protocol the proxy negotiated with the client (TLV `0x01`).
216    #[must_use]
217    pub fn alpn(&self) -> Option<&'a [u8]> {
218        self.find(TLV_ALPN)
219    }
220
221    /// The authority (host name) the client asked for (TLV `0x02`), if UTF-8.
222    #[must_use]
223    pub fn authority(&self) -> Option<&'a str> {
224        self.find(TLV_AUTHORITY)
225            .and_then(|v| str::from_utf8(v).ok())
226    }
227
228    /// The proxy's unique connection id (TLV `0x05`, at most 128 bytes).
229    #[must_use]
230    pub fn unique_id(&self) -> Option<&'a [u8]> {
231        self.find(TLV_UNIQUE_ID)
232    }
233
234    /// The SSL facts (TLV `0x20`).
235    #[must_use]
236    pub fn ssl(&self) -> Option<ProxySsl<'a>> {
237        let v = self.find(TLV_SSL)?;
238        let client = v[0];
239        let verify = u32::from_be_bytes([v[1], v[2], v[3], v[4]]);
240        let mut ssl = ProxySsl {
241            client,
242            verified: verify == 0,
243            version: None,
244            common_name: None,
245            cipher: None,
246            signature_algorithm: None,
247            key_algorithm: None,
248        };
249        let mut rest = &v[5..];
250        while let Some((sub, more)) = split_tlv(rest) {
251            let text = str::from_utf8(sub.value).ok();
252            match sub.kind {
253                SSL_VERSION => ssl.version = text,
254                SSL_CN => ssl.common_name = text,
255                SSL_CIPHER => ssl.cipher = text,
256                SSL_SIG_ALG => ssl.signature_algorithm = text,
257                SSL_KEY_ALG => ssl.key_algorithm = text,
258                _ => {}
259            }
260            rest = more;
261        }
262        Some(ssl)
263    }
264
265    /// The network namespace (TLV `0x30`).
266    #[must_use]
267    pub fn network_namespace(&self) -> Option<&'a [u8]> {
268        self.find(TLV_NETNS)
269    }
270
271    /// Whether a CRC32C TLV was present and matched.
272    #[must_use]
273    pub fn crc_checked(&self) -> bool {
274        self.crc_checked
275    }
276
277    /// The facts net-io keeps after the header's bytes are gone (fixed arrays;
278    /// a value longer than its array is left out).
279    #[must_use]
280    pub fn to_facts(&self) -> ProxyFacts {
281        ProxyFacts::from_parts(
282            self.alpn(),
283            self.authority(),
284            self.unique_id(),
285            self.ssl().map(|s| s.client),
286        )
287    }
288}
289
290impl ProxyParser {
291    /// A parser accepting `versions`, refusing a v2 header whose body (after the
292    /// 16 fixed bytes) is longer than `v2_body_max`.
293    #[must_use]
294    pub fn new(versions: ProxyVersions, v2_body_max: u16) -> ProxyParser {
295        ProxyParser {
296            versions,
297            v2_body_max,
298            kind: Kind::Undecided,
299            scanned: 0,
300        }
301    }
302
303    /// Parses from `input`, which holds every byte received so far.
304    ///
305    /// # Errors
306    ///
307    /// [`ProxyError`]: the bytes are not an allowed header, or are malformed.
308    pub fn feed<'a>(&mut self, input: &'a [u8]) -> Result<ProxyProgress<'a>, ProxyError> {
309        if self.kind == Kind::Undecided {
310            let Some(&first) = input.first() else {
311                return Ok(ProxyProgress::NeedMore { at_least: 1 });
312            };
313            let v1 = matches!(self.versions, ProxyVersions::V1 | ProxyVersions::Both);
314            let v2 = matches!(self.versions, ProxyVersions::V2 | ProxyVersions::Both);
315            self.kind = match first {
316                b'P' if v1 => Kind::V1,
317                0x0D if v2 => Kind::V2,
318                _ => return Err(ProxyError::NotProxy),
319            };
320        }
321        match self.kind {
322            Kind::V1 => self.feed_v1(input),
323            _ => self.feed_v2(input),
324        }
325    }
326
327    fn feed_v1<'a>(&mut self, input: &'a [u8]) -> Result<ProxyProgress<'a>, ProxyError> {
328        let n = input.len().min(V1_PREFIX.len());
329        if input[..n] != V1_PREFIX[..n] {
330            return Err(ProxyError::NotProxy);
331        }
332        let limit = input.len().min(V1_MAX);
333        let start = self.scanned.min(limit);
334        let Some(lf) = input[start..limit].iter().position(|&b| b == b'\n') else {
335            if input.len() >= V1_MAX {
336                return Err(ProxyError::V1TooLong);
337            }
338            self.scanned = limit;
339            return Ok(ProxyProgress::NeedMore { at_least: 1 });
340        };
341        let lf = start + lf;
342        if lf == 0 || input[lf - 1] != b'\r' {
343            return Err(ProxyError::V1Syntax { at: offset(lf) });
344        }
345        let header = parse_v1_line(&input[..lf - 1])?;
346        Ok(ProxyProgress::Done {
347            header,
348            consumed: lf + 1,
349        })
350    }
351
352    fn feed_v2<'a>(&mut self, input: &'a [u8]) -> Result<ProxyProgress<'a>, ProxyError> {
353        let n = input.len().min(V2_SIGNATURE.len());
354        if input[..n] != V2_SIGNATURE[..n] {
355            return Err(ProxyError::NotProxy);
356        }
357        if input.len() < V2_FIXED {
358            return Ok(ProxyProgress::NeedMore {
359                at_least: V2_FIXED - input.len(),
360            });
361        }
362        let version = input[12] >> 4;
363        if version != 2 {
364            return Err(ProxyError::V2BadVersion);
365        }
366        let command = match input[12] & 0x0F {
367            0x0 => ProxyCommand::Local,
368            0x1 => ProxyCommand::Proxy,
369            _ => return Err(ProxyError::V2BadCommand),
370        };
371        let family = input[13];
372        let (address_len, transport) = match family {
373            0x00 => (0, ProxyTransport::Unspecified),
374            0x11 => (12, ProxyTransport::Stream),
375            0x12 => (12, ProxyTransport::Datagram),
376            0x21 => (36, ProxyTransport::Stream),
377            0x22 => (36, ProxyTransport::Datagram),
378            0x31 => (216, ProxyTransport::Stream),
379            0x32 => (216, ProxyTransport::Datagram),
380            _ => return Err(ProxyError::V2BadFamily),
381        };
382        let declared = u16::from_be_bytes([input[14], input[15]]);
383        if declared > self.v2_body_max {
384            return Err(ProxyError::V2LengthTooLarge {
385                declared,
386                max: self.v2_body_max,
387            });
388        }
389        let total = V2_FIXED + usize::from(declared);
390        if input.len() < total {
391            return Ok(ProxyProgress::NeedMore {
392                at_least: total - input.len(),
393            });
394        }
395        let header = &input[..total];
396        let body = &header[V2_FIXED..];
397        if body.len() < address_len {
398            return Err(ProxyError::V2AddressTooShort);
399        }
400        let (source, destination) = if command == ProxyCommand::Proxy {
401            v2_addresses(family >> 4, &body[..address_len])
402        } else {
403            (None, None)
404        };
405        let tlv_bytes = &body[address_len..];
406        let crc_checked = check_tlvs(header, V2_FIXED + address_len)?;
407        Ok(ProxyProgress::Done {
408            header: ProxyHeader {
409                version: 2,
410                command,
411                transport,
412                source,
413                destination,
414                tlvs: Tlvs { rest: tlv_bytes },
415                crc_checked,
416            },
417            consumed: total,
418        })
419    }
420}
421
422/// A byte offset within a v1 line, which is at most 107 bytes.
423fn offset(at: usize) -> u8 {
424    u8::try_from(at).unwrap_or(u8::MAX)
425}
426
427/// One TLV from the front of `bytes`, or `None` when fewer than a whole TLV remain.
428fn split_tlv(bytes: &[u8]) -> Option<(Tlv<'_>, &[u8])> {
429    if bytes.len() < 3 {
430        return None;
431    }
432    let len = usize::from(u16::from_be_bytes([bytes[1], bytes[2]]));
433    let value = bytes.get(3..3 + len)?;
434    Some((
435        Tlv {
436            kind: bytes[0],
437            value,
438        },
439        &bytes[3 + len..],
440    ))
441}
442
443/// Walks every TLV of `header` (starting at `start`) and checks it ends exactly
444/// at the declared length; verifies CRC32C, the unique id, and the SSL TLV.
445/// Returns whether a checksum was verified.
446fn check_tlvs(header: &[u8], start: usize) -> Result<bool, ProxyError> {
447    let mut pos = start;
448    let mut crc_checked = false;
449    while pos < header.len() {
450        let rest = &header[pos..];
451        let Some((tlv, _)) = split_tlv(rest) else {
452            return Err(ProxyError::V2TlvOverrun);
453        };
454        let value_at = pos + 3;
455        match tlv.kind {
456            TLV_CRC32C => {
457                if tlv.value.len() != 4 {
458                    return Err(ProxyError::V2CrcMismatch);
459                }
460                let expected =
461                    u32::from_be_bytes([tlv.value[0], tlv.value[1], tlv.value[2], tlv.value[3]]);
462                let mut c = Crc32c::new();
463                c.update(&header[..value_at]);
464                c.update(&[0; 4]);
465                c.update(&header[value_at + 4..]);
466                if c.finish() != expected {
467                    return Err(ProxyError::V2CrcMismatch);
468                }
469                crc_checked = true;
470            }
471            TLV_UNIQUE_ID if tlv.value.len() > UNIQUE_ID_MAX => {
472                return Err(ProxyError::V2UniqueIdTooLong);
473            }
474            TLV_SSL => check_ssl(tlv.value)?,
475            _ => {}
476        }
477        pos = value_at + tlv.value.len();
478    }
479    Ok(crc_checked)
480}
481
482fn check_ssl(value: &[u8]) -> Result<(), ProxyError> {
483    if value.len() < 5 {
484        return Err(ProxyError::V2SslMalformed);
485    }
486    let mut rest = &value[5..];
487    while !rest.is_empty() {
488        match split_tlv(rest) {
489            Some((_, more)) => rest = more,
490            None => return Err(ProxyError::V2SslMalformed),
491        }
492    }
493    Ok(())
494}
495
496fn v2_addresses(family: u8, block: &[u8]) -> (Option<ProxyAddress<'_>>, Option<ProxyAddress<'_>>) {
497    let port = |b: &[u8]| u16::from_be_bytes([b[0], b[1]]);
498    match family {
499        0x1 => {
500            let src = Ipv4Addr::new(block[0], block[1], block[2], block[3]);
501            let dst = Ipv4Addr::new(block[4], block[5], block[6], block[7]);
502            (
503                Some(ProxyAddress::Ip(SocketAddr::new(
504                    IpAddr::V4(src),
505                    port(&block[8..]),
506                ))),
507                Some(ProxyAddress::Ip(SocketAddr::new(
508                    IpAddr::V4(dst),
509                    port(&block[10..]),
510                ))),
511            )
512        }
513        0x2 => {
514            let mut s = [0u8; 16];
515            let mut d = [0u8; 16];
516            s.copy_from_slice(&block[..16]);
517            d.copy_from_slice(&block[16..32]);
518            (
519                Some(ProxyAddress::Ip(SocketAddr::new(
520                    IpAddr::V6(Ipv6Addr::from(s)),
521                    port(&block[32..]),
522                ))),
523                Some(ProxyAddress::Ip(SocketAddr::new(
524                    IpAddr::V6(Ipv6Addr::from(d)),
525                    port(&block[34..]),
526                ))),
527            )
528        }
529        0x3 => (
530            Some(ProxyAddress::Unix(trim_zeros(&block[..108]))),
531            Some(ProxyAddress::Unix(trim_zeros(&block[108..216]))),
532        ),
533        _ => (None, None),
534    }
535}
536
537fn trim_zeros(path: &[u8]) -> &[u8] {
538    let end = path.iter().rposition(|&b| b != 0).map_or(0, |i| i + 1);
539    &path[..end]
540}
541
542/// Parses a v1 line without its CRLF.
543fn parse_v1_line(line: &[u8]) -> Result<ProxyHeader<'static>, ProxyError> {
544    let rest = &line[V1_PREFIX.len()..];
545    let base = V1_PREFIX.len();
546    let empty = Tlvs { rest: &[] };
547    if let Some(after) = rest.strip_prefix(b"UNKNOWN") {
548        // The receiver ignores everything after UNKNOWN on the line.
549        if after.is_empty() || after[0] == b' ' {
550            return Ok(ProxyHeader {
551                version: 1,
552                command: ProxyCommand::Proxy,
553                transport: ProxyTransport::Unspecified,
554                source: None,
555                destination: None,
556                tlvs: empty,
557                crc_checked: false,
558            });
559        }
560        return Err(ProxyError::V1Syntax {
561            at: offset(base + 7),
562        });
563    }
564    let mut fields = [&[][..]; 5];
565    let mut count = 0;
566    let mut at = base;
567    for field in rest.split(|&b| b == b' ') {
568        if field.is_empty() || count == 5 {
569            return Err(ProxyError::V1Syntax { at: offset(at) });
570        }
571        fields[count] = field;
572        count += 1;
573        at += field.len() + 1;
574    }
575    if count != 5 {
576        return Err(ProxyError::V1Syntax {
577            at: offset(line.len()),
578        });
579    }
580    let v6 = match fields[0] {
581        b"TCP4" => false,
582        b"TCP6" => true,
583        _ => return Err(ProxyError::V1Syntax { at: offset(base) }),
584    };
585    let address = |text: &[u8]| -> Result<IpAddr, ProxyError> {
586        let (mine, other) = if v6 {
587            (
588                parse_ipv6(text).map(IpAddr::V6),
589                parse_ipv4(text).map(IpAddr::V4),
590            )
591        } else {
592            (
593                parse_ipv4(text).map(IpAddr::V4),
594                parse_ipv6(text).map(IpAddr::V6),
595            )
596        };
597        match (mine, other) {
598            (Some(ip), _) => Ok(ip),
599            (None, Some(_)) => Err(ProxyError::FamilyMismatch),
600            (None, None) => Err(ProxyError::V1BadAddress),
601        }
602    };
603    let src = address(fields[1])?;
604    let dst = address(fields[2])?;
605    let sport = parse_port(fields[3]).ok_or(ProxyError::V1BadPort)?;
606    let dport = parse_port(fields[4]).ok_or(ProxyError::V1BadPort)?;
607    Ok(ProxyHeader {
608        version: 1,
609        command: ProxyCommand::Proxy,
610        transport: ProxyTransport::Stream,
611        source: Some(ProxyAddress::Ip(SocketAddr::new(src, sport))),
612        destination: Some(ProxyAddress::Ip(SocketAddr::new(dst, dport))),
613        tlvs: empty,
614        crc_checked: false,
615    })
616}
617
618/// A decimal port, 0 to 65535, no sign and no leading zeros.
619fn parse_port(text: &[u8]) -> Option<u16> {
620    decimal(text, 5).and_then(|v| u16::try_from(v).ok())
621}
622
623/// Decimal digits only, at most `max_digits`, no leading zero unless the value is 0.
624fn decimal(text: &[u8], max_digits: usize) -> Option<u32> {
625    if text.is_empty() || text.len() > max_digits || (text.len() > 1 && text[0] == b'0') {
626        return None;
627    }
628    text.iter().try_fold(0u32, |acc, &b| {
629        b.is_ascii_digit().then(|| acc * 10 + u32::from(b - b'0'))
630    })
631}
632
633/// A dotted-quad IPv4 address: four decimal octets, no leading zeros.
634fn parse_ipv4(text: &[u8]) -> Option<Ipv4Addr> {
635    let mut octets = [0u8; 4];
636    let mut n = 0;
637    for part in text.split(|&b| b == b'.') {
638        if n == 4 {
639            return None;
640        }
641        octets[n] = u8::try_from(decimal(part, 3)?).ok()?;
642        n += 1;
643    }
644    (n == 4).then(|| Ipv4Addr::from(octets))
645}
646
647/// An IPv6 address in RFC 4291 text form: eight groups of one to four hex
648/// digits, at most one `::`, optionally ending in a dotted-quad IPv4 address.
649/// No zone index.
650fn parse_ipv6(text: &[u8]) -> Option<Ipv6Addr> {
651    let (head, tail, compressed) = match find_double_colon(text) {
652        Some(i) => (&text[..i], &text[i + 2..], true),
653        None => (text, &[][..], false),
654    };
655    if compressed && find_double_colon(tail).is_some() {
656        return None;
657    }
658    let mut groups = [0u16; 8];
659    // Groups before `::` (or all of them, when there is no `::`).
660    let front = groups_into(head, &mut groups, 0, !compressed)?;
661    if !compressed {
662        return (front == 8).then(|| Ipv6Addr::from(groups));
663    }
664    let mut back = [0u16; 8];
665    let n_back = groups_into(tail, &mut back, 0, true)?;
666    if front + n_back > 7 {
667        return None;
668    }
669    groups[8 - n_back..].copy_from_slice(&back[..n_back]);
670    Some(Ipv6Addr::from(groups))
671}
672
673fn find_double_colon(text: &[u8]) -> Option<usize> {
674    text.windows(2).position(|w| w == b"::")
675}
676
677/// Parses `:`-separated groups of `text` into `out` from index `at`; an IPv4
678/// tail (two groups) is allowed when `last` says this part ends the address.
679/// Returns the next free index.
680fn groups_into(text: &[u8], out: &mut [u16; 8], mut at: usize, last: bool) -> Option<usize> {
681    if text.is_empty() {
682        return Some(at);
683    }
684    let mut parts = text.split(|&b| b == b':').peekable();
685    while let Some(part) = parts.next() {
686        let is_last = parts.peek().is_none();
687        if is_last && last && part.contains(&b'.') {
688            let v4 = parse_ipv4(part)?.octets();
689            if at + 2 > 8 {
690                return None;
691            }
692            out[at] = u16::from_be_bytes([v4[0], v4[1]]);
693            out[at + 1] = u16::from_be_bytes([v4[2], v4[3]]);
694            at += 2;
695            continue;
696        }
697        if part.is_empty() || part.len() > 4 || at >= 8 {
698            return None;
699        }
700        let mut v = 0u16;
701        for &b in part {
702            let d = (b as char).to_digit(16)?;
703            v = (v << 4) | u16::try_from(d).ok()?;
704        }
705        out[at] = v;
706        at += 1;
707    }
708    Some(at)
709}
710
711#[cfg(test)]
712mod tests {
713    use super::*;
714
715    fn done(p: &mut ProxyParser, bytes: &[u8]) -> (ProxyHeader<'static>, usize) {
716        let bytes: &'static [u8] = Box::leak(bytes.to_vec().into_boxed_slice());
717        match p.feed(bytes).unwrap() {
718            ProxyProgress::Done { header, consumed } => (header, consumed),
719            ProxyProgress::NeedMore { at_least } => panic!("need {at_least} more"),
720        }
721    }
722
723    fn v1(line: &str) -> Result<ProxyHeader<'static>, ProxyError> {
724        let bytes: &'static [u8] = Box::leak(line.as_bytes().to_vec().into_boxed_slice());
725        match ProxyParser::new(ProxyVersions::Both, 1024).feed(bytes)? {
726            ProxyProgress::Done { header, .. } => Ok(header),
727            ProxyProgress::NeedMore { .. } => panic!("incomplete: {line:?}"),
728        }
729    }
730
731    /// Builds a v2 header: command, family, address block, TLVs; optionally a CRC.
732    fn v2(command: u8, family: u8, block: &[u8], tlvs: &[(u8, &[u8])], crc: bool) -> Vec<u8> {
733        let mut body = block.to_vec();
734        for (kind, value) in tlvs {
735            body.push(*kind);
736            body.extend_from_slice(&u16::try_from(value.len()).unwrap().to_be_bytes());
737            body.extend_from_slice(value);
738        }
739        let crc_at = if crc {
740            body.extend_from_slice(&[TLV_CRC32C, 0, 4, 0, 0, 0, 0]);
741            Some(V2_FIXED + body.len() - 4)
742        } else {
743            None
744        };
745        let mut h = V2_SIGNATURE.to_vec();
746        h.push(0x20 | command);
747        h.push(family);
748        h.extend_from_slice(&u16::try_from(body.len()).unwrap().to_be_bytes());
749        h.extend_from_slice(&body);
750        if let Some(at) = crc_at {
751            let sum = crate::crc32c::crc32c(&h);
752            h[at..at + 4].copy_from_slice(&sum.to_be_bytes());
753        }
754        h
755    }
756
757    fn ipv4_block() -> Vec<u8> {
758        let mut b = vec![192, 0, 2, 1, 198, 51, 100, 2];
759        b.extend_from_slice(&51000u16.to_be_bytes());
760        b.extend_from_slice(&443u16.to_be_bytes());
761        b
762    }
763
764    #[test]
765    fn test_v1_tcp4_and_rest_is_data() {
766        let mut p = ProxyParser::new(ProxyVersions::Both, 1024);
767        let (h, consumed) = done(
768            &mut p,
769            b"PROXY TCP4 192.0.2.1 198.51.100.2 51000 443\r\nGET",
770        );
771        assert_eq!(h.version, 1);
772        assert_eq!(
773            h.source,
774            Some(ProxyAddress::Ip("192.0.2.1:51000".parse().unwrap()))
775        );
776        assert_eq!(
777            h.destination,
778            Some(ProxyAddress::Ip("198.51.100.2:443".parse().unwrap()))
779        );
780        assert_eq!(consumed, 45);
781    }
782
783    #[test]
784    fn test_v1_tcp6_unknown_and_mapped() {
785        let h = v1("PROXY TCP6 2001:db8::1 ::ffff:192.0.2.1 1 65535\r\n").unwrap();
786        assert_eq!(h.source.unwrap().to_string(), "[2001:db8::1]:1");
787        let h = v1("PROXY UNKNOWN\r\n").unwrap();
788        assert!(h.source.is_none());
789        let h = v1("PROXY UNKNOWN anything at all 1 2\r\n").unwrap();
790        assert_eq!(h.transport, ProxyTransport::Unspecified);
791    }
792
793    #[test]
794    fn test_v1_strict_grammar() {
795        use ProxyError::*;
796        let bad = [
797            ("PROXY TCP4  192.0.2.1 198.51.100.2 1 2\r\n", "double space"),
798            (
799                "PROXY TCP4 192.0.2.1 198.51.100.2 1 2 \r\n",
800                "trailing space",
801            ),
802            ("PROXY TCP4 192.0.2.1 198.51.100.2 1\r\n", "missing port"),
803            ("PROXY TCP5 192.0.2.1 198.51.100.2 1 2\r\n", "protocol"),
804            ("PROXY UNKNOWNX\r\n", "unknown suffix"),
805        ];
806        for (line, why) in bad {
807            assert!(
808                matches!(v1(line), Err(V1Syntax { .. })),
809                "{why}: {:?}",
810                v1(line)
811            );
812        }
813        assert_eq!(
814            v1("PROXY TCP4 192.0.2.01 198.51.100.2 1 2\r\n").unwrap_err(),
815            V1BadAddress
816        );
817        assert_eq!(
818            v1("PROXY TCP4 256.0.2.1 198.51.100.2 1 2\r\n").unwrap_err(),
819            V1BadAddress
820        );
821        assert_eq!(
822            v1("PROXY TCP4 192.0.2.1 198.51.100.2 01 2\r\n").unwrap_err(),
823            V1BadPort
824        );
825        assert_eq!(
826            v1("PROXY TCP4 192.0.2.1 198.51.100.2 +1 2\r\n").unwrap_err(),
827            V1BadPort
828        );
829        assert_eq!(
830            v1("PROXY TCP4 192.0.2.1 198.51.100.2 65536 2\r\n").unwrap_err(),
831            V1BadPort
832        );
833        assert_eq!(
834            v1("PROXY TCP4 2001:db8::1 198.51.100.2 1 2\r\n").unwrap_err(),
835            FamilyMismatch
836        );
837        assert_eq!(
838            v1("PROXY TCP6 192.0.2.1 ::1 1 2\r\n").unwrap_err(),
839            FamilyMismatch
840        );
841        assert!(matches!(
842            ProxyParser::new(ProxyVersions::Both, 1024).feed(b"PROXY TCP4 1.2.3.4 1.2.3.4 1 2\n"),
843            Err(V1Syntax { .. })
844        ));
845    }
846
847    /// Edge case 13: 107 bytes without a line end.
848    #[test]
849    fn test_v1_too_long() {
850        let mut line = b"PROXY UNKNOWN ".to_vec();
851        line.resize(107, b'x');
852        let r = ProxyParser::new(ProxyVersions::V1, 1024).feed(&line);
853        assert_eq!(r.unwrap_err(), ProxyError::V1TooLong);
854        let mut ok = b"PROXY UNKNOWN ".to_vec();
855        ok.resize(105, b'x');
856        ok.extend_from_slice(b"\r\n");
857        assert!(matches!(
858            ProxyParser::new(ProxyVersions::V1, 1024).feed(&ok),
859            Ok(ProxyProgress::Done { consumed: 107, .. })
860        ));
861    }
862
863    #[test]
864    fn test_not_proxy_and_version_filters() {
865        let mut p = ProxyParser::new(ProxyVersions::Both, 1024);
866        assert_eq!(p.feed(b"GET / HTTP/1.1").unwrap_err(), ProxyError::NotProxy);
867        assert_eq!(
868            ProxyParser::new(ProxyVersions::Both, 1024)
869                .feed(b"PROXX")
870                .unwrap_err(),
871            ProxyError::NotProxy
872        );
873        let v2h = v2(1, 0x11, &ipv4_block(), &[], false);
874        assert_eq!(
875            ProxyParser::new(ProxyVersions::V1, 1024)
876                .feed(&v2h)
877                .unwrap_err(),
878            ProxyError::NotProxy
879        );
880        assert_eq!(
881            ProxyParser::new(ProxyVersions::V2, 1024)
882                .feed(b"PROXY TCP4")
883                .unwrap_err(),
884            ProxyError::NotProxy
885        );
886    }
887
888    #[test]
889    fn test_v2_ipv4_tlvs_and_crc() {
890        let h = v2(
891            1,
892            0x11,
893            &ipv4_block(),
894            &[
895                (TLV_ALPN, b"h2"),
896                (TLV_NOOP, b"pad"),
897                (TLV_AUTHORITY, b"example.com"),
898                (TLV_UNIQUE_ID, b"id-1"),
899                (
900                    TLV_SSL,
901                    &[
902                        0x07,
903                        0,
904                        0,
905                        0,
906                        0,
907                        SSL_VERSION,
908                        0,
909                        7,
910                        b'T',
911                        b'L',
912                        b'S',
913                        b'v',
914                        b'1',
915                        b'.',
916                        b'3',
917                        SSL_CN,
918                        0,
919                        3,
920                        b'b',
921                        b'o',
922                        b'b',
923                    ],
924                ),
925                (0xE0, b"custom"),
926            ],
927            true,
928        );
929        let mut p = ProxyParser::new(ProxyVersions::Both, 1024);
930        let (header, consumed) = done(&mut p, &h);
931        assert_eq!(consumed, h.len());
932        assert_eq!(header.command, ProxyCommand::Proxy);
933        assert_eq!(header.source.unwrap().to_string(), "192.0.2.1:51000");
934        assert_eq!(header.alpn(), Some(&b"h2"[..]));
935        assert_eq!(header.authority(), Some("example.com"));
936        assert_eq!(header.unique_id(), Some(&b"id-1"[..]));
937        assert!(header.crc_checked());
938        let ssl = header.ssl().unwrap();
939        assert_eq!(
940            (ssl.client, ssl.verified, ssl.version, ssl.common_name),
941            (7, true, Some("TLSv1.3"), Some("bob"))
942        );
943        assert!({ header.tlvs }.all(|t| t.kind != TLV_NOOP), "NOOP skipped");
944        assert!({ header.tlvs }.any(|t| t.kind == 0xE0 && t.value == b"custom"));
945        let facts = header.to_facts();
946        assert_eq!(facts.alpn(), Some(&b"h2"[..]));
947        assert_eq!(facts.authority(), Some("example.com"));
948        assert_eq!(facts.ssl_client(), Some(7));
949    }
950
951    #[test]
952    fn test_v2_ipv6_unix_local_unspec() {
953        let mut block = vec![0u8; 36];
954        block[15] = 1;
955        block[31] = 2;
956        block[32..34].copy_from_slice(&80u16.to_be_bytes());
957        let h = v2(1, 0x21, &block, &[], false);
958        let (hd, _) = done(&mut ProxyParser::new(ProxyVersions::V2, 1024), &h);
959        assert_eq!(hd.source.unwrap().to_string(), "[::1]:80");
960        let mut unix = vec![0u8; 216];
961        unix[..9].copy_from_slice(b"/run/a.sk");
962        let h = v2(1, 0x31, &unix, &[], false);
963        let (hd, _) = done(&mut ProxyParser::new(ProxyVersions::V2, 1024), &h);
964        assert_eq!(hd.source, Some(ProxyAddress::Unix(b"/run/a.sk")));
965        let h = v2(0, 0x11, &ipv4_block(), &[], false);
966        let (hd, _) = done(&mut ProxyParser::new(ProxyVersions::V2, 1024), &h);
967        assert_eq!((hd.command, hd.source), (ProxyCommand::Local, None));
968        let h = v2(1, 0x00, &[], &[], false);
969        let (hd, _) = done(&mut ProxyParser::new(ProxyVersions::V2, 1024), &h);
970        assert_eq!(
971            (hd.transport, hd.source),
972            (ProxyTransport::Unspecified, None)
973        );
974    }
975
976    #[test]
977    fn test_v2_errors() {
978        use ProxyError::*;
979        let feed = |h: &[u8]| ProxyParser::new(ProxyVersions::V2, 64).feed(h).map(|_| ());
980        let mut h = v2(1, 0x11, &ipv4_block(), &[], false);
981        h[12] = 0x11;
982        assert_eq!(feed(&h).unwrap_err(), V2BadVersion);
983        let mut h = v2(1, 0x11, &ipv4_block(), &[], false);
984        h[12] = 0x22;
985        assert_eq!(feed(&h).unwrap_err(), V2BadCommand);
986        let mut h = v2(1, 0x11, &ipv4_block(), &[], false);
987        h[13] = 0x13;
988        assert_eq!(feed(&h).unwrap_err(), V2BadFamily);
989        let big = v2(1, 0x11, &ipv4_block(), &[(0xE0, &[0u8; 100])], false);
990        assert_eq!(
991            feed(&big).unwrap_err(),
992            V2LengthTooLarge {
993                declared: 115,
994                max: 64
995            }
996        );
997        let short = v2(1, 0x21, &[0u8; 20], &[], false);
998        assert_eq!(feed(&short).unwrap_err(), V2AddressTooShort);
999        let mut over = v2(1, 0x11, &ipv4_block(), &[(0xE0, b"abc")], false);
1000        let n = over.len();
1001        over[n - 4] = 9; // TLV length runs past the end
1002        assert_eq!(feed(&over).unwrap_err(), V2TlvOverrun);
1003        let mut crc = v2(1, 0x11, &ipv4_block(), &[(TLV_ALPN, b"h2")], true);
1004        let n = crc.len();
1005        crc[n - 1] ^= 1;
1006        assert_eq!(feed(&crc).unwrap_err(), V2CrcMismatch);
1007        let long_id = vec![b'i'; 129];
1008        let h = v2(1, 0x11, &ipv4_block(), &[(TLV_UNIQUE_ID, &long_id)], false);
1009        assert_eq!(
1010            ProxyParser::new(ProxyVersions::V2, 1024)
1011                .feed(&h)
1012                .map(|_| ())
1013                .unwrap_err(),
1014            V2UniqueIdTooLong
1015        );
1016        let h = v2(1, 0x11, &ipv4_block(), &[(TLV_SSL, &[1, 0, 0])], false);
1017        assert_eq!(feed(&h).unwrap_err(), V2SslMalformed);
1018    }
1019
1020    /// Edge case 14: a v2 header split across 12 segments.
1021    #[test]
1022    fn test_v2_split_into_segments_waits_then_succeeds() {
1023        let mut h = v2(
1024            1,
1025            0x11,
1026            &ipv4_block(),
1027            &[(TLV_AUTHORITY, b"a.example")],
1028            true,
1029        );
1030        h.extend_from_slice(b"DATA");
1031        let total = h.len() - 4;
1032        let mut p = ProxyParser::new(ProxyVersions::Both, 1024);
1033        let step = h.len().div_ceil(12);
1034        let mut have = 0;
1035        loop {
1036            have = (have + step).min(h.len());
1037            match p.feed(&h[..have]).unwrap() {
1038                ProxyProgress::NeedMore { at_least } => assert!(at_least > 0 && have < h.len()),
1039                ProxyProgress::Done { consumed, .. } => {
1040                    assert_eq!(consumed, total);
1041                    break;
1042                }
1043            }
1044        }
1045    }
1046
1047    /// Edge cases 15 and 16: the header and data in one segment, and a second
1048    /// header is application data.
1049    #[test]
1050    fn test_second_header_is_data() {
1051        let two = b"PROXY UNKNOWN\r\nPROXY UNKNOWN\r\n";
1052        let mut p = ProxyParser::new(ProxyVersions::V1, 1024);
1053        let (_, consumed) = done(&mut p, two);
1054        assert_eq!(&two[consumed..], b"PROXY UNKNOWN\r\n");
1055    }
1056
1057    /// Property: every split of a valid header gives the same result as the whole.
1058    #[test]
1059    fn prop_every_split_point_agrees() {
1060        let headers: Vec<Vec<u8>> = vec![
1061            b"PROXY TCP6 2001:db8:0:0:1:0:0:1 ::ffff:10.0.0.1 1024 8080\r\n".to_vec(),
1062            v2(
1063                1,
1064                0x11,
1065                &ipv4_block(),
1066                &[(TLV_ALPN, b"http/1.1"), (TLV_UNIQUE_ID, b"u")],
1067                true,
1068            ),
1069        ];
1070        for h in headers {
1071            for split in 0..h.len() {
1072                let mut p = ProxyParser::new(ProxyVersions::Both, 1024);
1073                if split > 0 {
1074                    assert!(
1075                        matches!(p.feed(&h[..split]).unwrap(), ProxyProgress::NeedMore { .. }),
1076                        "split {split}"
1077                    );
1078                }
1079                match p.feed(&h).unwrap() {
1080                    ProxyProgress::Done { consumed, .. } => assert_eq!(consumed, h.len()),
1081                    ProxyProgress::NeedMore { .. } => {
1082                        panic!("whole header incomplete at split {split}")
1083                    }
1084                }
1085            }
1086        }
1087    }
1088
1089    /// Property: random bytes never panic, and a header is never longer than the input.
1090    #[test]
1091    fn prop_random_input_never_panics() {
1092        use crate::testing::Gen;
1093        let mut g = Gen::new(0x9F0C_7E11);
1094        for _ in 0..if cfg!(miri) { 200 } else { 50_000 } {
1095            let len = g.below(300) as usize;
1096            let mut bytes: Vec<u8> = (0..len).map(|_| g.next_u64() as u8).collect();
1097            match g.below(3) {
1098                0 if len >= 12 => bytes[..12].copy_from_slice(&V2_SIGNATURE),
1099                1 if len >= 6 => bytes[..6].copy_from_slice(V1_PREFIX),
1100                _ => {}
1101            }
1102            let mut p = ProxyParser::new(ProxyVersions::Both, u16::MAX);
1103            if let Ok(ProxyProgress::Done { consumed, header }) = p.feed(&bytes) {
1104                assert!(consumed <= bytes.len());
1105                let _ = (header.alpn(), header.ssl(), header.tlvs.count());
1106            }
1107        }
1108    }
1109
1110    /// Property: the IPv6 grammar accepts only what std accepts, with the same value,
1111    /// and accepts every address std prints.
1112    #[test]
1113    fn prop_ipv6_grammar_matches_std() {
1114        use crate::testing::Gen;
1115        let mut g = Gen::new(0x1FE6_0001);
1116        let alphabet = b"0123456789abcdefABCDEF:.";
1117        for _ in 0..if cfg!(miri) { 200 } else { 100_000 } {
1118            let len = 1 + g.below(40) as usize;
1119            let text: Vec<u8> = (0..len)
1120                .map(|_| alphabet[g.below(alphabet.len() as u64) as usize])
1121                .collect();
1122            if let Some(ours) = parse_ipv6(&text) {
1123                let s = str::from_utf8(&text).unwrap();
1124                assert_eq!(s.parse::<Ipv6Addr>().ok(), Some(ours), "{s}");
1125            }
1126        }
1127        for _ in 0..if cfg!(miri) { 200 } else { 20_000 } {
1128            let mut b = [0u8; 16];
1129            for x in &mut b {
1130                // Runs of zeros, so `::` forms appear.
1131                *x = if g.chance(2) { 0 } else { g.next_u64() as u8 };
1132            }
1133            let a = Ipv6Addr::from(b);
1134            assert_eq!(parse_ipv6(a.to_string().as_bytes()), Some(a), "{a}");
1135        }
1136        for (s, ok) in [
1137            ("::", true),
1138            ("::1", true),
1139            ("1::", true),
1140            ("1:2:3:4:5:6:7:8", true),
1141            ("1:2:3:4:5:6:7::", true),
1142            ("1:2:3:4:5:6:1.2.3.4", true),
1143            (":1", false),
1144            ("1:", false),
1145            ("1:::2", false),
1146            ("1::2::3", false),
1147            ("1:2:3:4:5:6:7:8:9", false),
1148            ("12345::", false),
1149            ("::1.2.3", false),
1150            ("fe80::1%eth0", false),
1151        ] {
1152            assert_eq!(parse_ipv6(s.as_bytes()).is_some(), ok, "{s}");
1153        }
1154    }
1155
1156    #[test]
1157    fn test_ipv4_grammar() {
1158        for (s, ok) in [
1159            ("0.0.0.0", true),
1160            ("255.255.255.255", true),
1161            ("1.2.3.04", false),
1162            ("1.2.3", false),
1163            ("1.2.3.4.5", false),
1164            ("1.2.3.256", false),
1165            ("1..3.4", false),
1166            (" 1.2.3.4", false),
1167        ] {
1168            assert_eq!(parse_ipv4(s.as_bytes()).is_some(), ok, "{s}");
1169        }
1170    }
1171}