proxy_protocol/
lib.rs

1//! # The HAProxy PROXY protocol.
2//!
3//! This defines a library to serialize and deserialize HAProxy PROXY-protocol
4//! headers.
5//!
6//! The protocol has been implemented per the specification available here:
7//! <https://www.haproxy.org/download/2.4/doc/proxy-protocol.txt>
8
9#![forbid(unsafe_code)]
10#![warn(rust_2018_idioms)]
11#![warn(clippy::all)]
12
13pub mod version1;
14pub mod version2;
15
16use bytes::{Buf, BytesMut};
17use snafu::{ensure, ResultExt as _, Snafu};
18
19#[derive(Debug, Snafu)]
20#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)] // A new version may be added
21#[cfg_attr(test, derive(PartialEq, Eq))]
22pub enum ParseError {
23    /// This is not a PROXY header at all.
24    #[snafu(display("the given data is not a PROXY header"))]
25    NotProxyHeader,
26
27    /// This version of the PROXY protocol is unsupported or impossible.
28    #[snafu(display("the version {} is invalid", version))]
29    InvalidVersion { version: u32 },
30
31    /// An error occurred while parsing version 1.
32    #[snafu(display("there was an error while parsing the v1 header: {}", source))]
33    Version1 { source: version1::ParseError },
34
35    /// An error occurred while parsing version 2.
36    #[snafu(display("there was an error while parsing the v2 header: {}", source))]
37    Version2 { source: version2::ParseError },
38}
39
40#[derive(Debug, Snafu)]
41#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)] // A new version may be added
42pub enum EncodeError {
43    /// An error occurred while encoding version 1.
44    #[snafu(display("there was an error while encoding the v1 header: {}", source))]
45    WriteVersion1 { source: version1::EncodeError },
46}
47
48/// The PROXY header emitted at most once at the start of a new connection.
49#[derive(Debug, Clone, PartialEq, Eq)]
50#[cfg_attr(not(feature = "always_exhaustive"), non_exhaustive)] // A new version may be added
51pub enum ProxyHeader {
52    /// This defines the first version specification, known as the
53    /// "human-readable header format" (section 2.1), and consists of (at most)
54    /// 107 bytes of data on the wire.
55    Version1 {
56        /// The addresses used to connect to the proxy.
57        addresses: version1::ProxyAddresses,
58    },
59
60    /// This defines the second version specification, known as the
61    /// "binary header format" (section 2.2), and consists of a dynamic amount
62    /// of bytes on the wire, depending on what information the sender wishes to
63    /// convey.
64    Version2 {
65        /// The command of this header.
66        command: version2::ProxyCommand,
67
68        /// The protocol over which the information was transferred originally.
69        transport_protocol: version2::ProxyTransportProtocol,
70
71        /// The addresses used to connect to the proxy.
72        addresses: version2::ProxyAddresses,
73    },
74}
75
76fn parse_version(buf: &mut impl Buf) -> Result<u32, ParseError> {
77    // There is a 6 byte header to v1, 12 byte to all binary versions.
78    ensure!(buf.remaining() >= 6, NotProxyHeader);
79
80    // V1 is the only version that starts with "PROXY" (0x50 0x52 0x4F 0x58
81    // 0x59), and we can therefore decide version based on that.
82    //
83    // We use ::chunk to not advance any bytes unnecessarily.
84    if buf.chunk()[..6] == [b'P', b'R', b'O', b'X', b'Y', b' '] {
85        buf.advance(6);
86        return Ok(1);
87    }
88
89    // Now we require 13: 12 for the prefix, 1 for the version + command
90    ensure!(buf.remaining() >= 13, NotProxyHeader);
91    ensure!(
92        buf.chunk()[..12]
93            == [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A],
94        NotProxyHeader
95    );
96    buf.advance(12);
97
98    // Note that we will now not advance the version byte on purpose, as it also
99    // contains the command.
100    //
101    // PANIC: This is safe because we've already checked we had 13 bytes
102    // available to us above, and we've only read 12 so far.
103    let version = buf.chunk()[0];
104
105    // Excerpt:
106    //
107    // > The next byte (the 13th one) is the protocol version and command.
108    // >
109    // > The highest four bits contains the version. As of this specification,
110    // > it must always be sent as \x2 and the receiver must only accept this
111    // > value.
112    let version = version >> 4;
113
114    // Interesting edge-case! This is the only time version 1 would be invalid.
115    if version == 1 {
116        return InvalidVersion { version: 1u32 }.fail();
117    }
118
119    Ok(version as u32)
120}
121
122/// Parse a PROXY header from the given buffer.
123///
124/// NOTE: The buffer must have a continuous representation of the inner data
125/// available through [Buf::chunk], at the very least for the header. Data that
126/// follows may be chunked as you wish.
127pub fn parse(buf: &mut impl Buf) -> Result<ProxyHeader, ParseError> {
128    let version = match parse_version(buf) {
129        Ok(ver) => ver,
130        Err(e) => return Err(e),
131    };
132
133    Ok(match version {
134        1 => self::version1::parse(buf).context(Version1)?,
135        2 => self::version2::parse(buf).context(Version2)?,
136        _ => return InvalidVersion { version }.fail(),
137    })
138}
139
140/// Encodes a PROXY header from the given header definition.
141///
142/// This will perform heap allocations; they're kept to a minimum, but there is
143/// no guarantee there will be only one.
144pub fn encode(header: ProxyHeader) -> Result<BytesMut, EncodeError> {
145    Ok(match header {
146        ProxyHeader::Version1 { addresses, .. } => {
147            version1::encode(addresses).context(WriteVersion1)?
148        }
149        ProxyHeader::Version2 {
150            command,
151            transport_protocol,
152            addresses,
153        } => version2::encode(command, transport_protocol, addresses),
154
155        #[allow(unreachable_patterns)] // May be required to be exhaustive.
156        _ => unimplemented!("Unimplemented version?"),
157    })
158}
159
160#[cfg(test)]
161mod parse_tests {
162    use super::*;
163    use crate::ProxyHeader;
164    use bytes::Bytes;
165    use pretty_assertions::assert_eq;
166    use rand::prelude::*;
167    use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
168
169    #[test]
170    fn test_version1() {
171        let unknown = Ok(ProxyHeader::Version1 {
172            addresses: version1::ProxyAddresses::Unknown,
173        });
174        assert_eq!(parse(&mut &b"PROXY UNKNOWN\r\n"[..]), unknown);
175        assert_eq!(
176            parse(&mut &b"PROXY UNKNOWN this is bogus data!\r\r\r\n"[..]),
177            unknown,
178        );
179        assert_eq!(
180            parse(&mut &b"PROXY UNKNOWN 192.168.0.1 192.168.1.1 123 321\r\n"[..]),
181            unknown,
182        );
183
184        let mut random = [0u8; 128];
185        rand::thread_rng().fill_bytes(&mut random);
186        let mut header = b"PROXY UNKNOWN ".to_vec();
187        header.extend(&random[..]);
188        header.extend(b"\r\n");
189        let mut buf = Bytes::from(header);
190        assert_eq!(parse(&mut buf), unknown);
191        assert!(!buf.has_remaining()); // Consume the ENTIRE header!
192
193        fn valid_v4(
194            (a, b, c, d): (u8, u8, u8, u8),
195            e: u16,
196            (f, g, h, i): (u8, u8, u8, u8),
197            j: u16,
198        ) -> ProxyHeader {
199            ProxyHeader::Version1 {
200                addresses: version1::ProxyAddresses::Ipv4 {
201                    source: SocketAddrV4::new(Ipv4Addr::new(a, b, c, d), e),
202                    destination: SocketAddrV4::new(Ipv4Addr::new(f, g, h, i), j),
203                },
204            }
205        }
206
207        assert_eq!(
208            parse(&mut &b"PROXY TCP4 192.168.201.102 1.2.3.4 0 65535\r\n"[..]),
209            Ok(valid_v4((192, 168, 201, 102), 0, (1, 2, 3, 4), 65535)),
210        );
211        assert_eq!(
212            parse(&mut &b"PROXY TCP4 0.0.0.0 0.0.0.0 0 0\r\n"[..]),
213            Ok(valid_v4((0, 0, 0, 0), 0, (0, 0, 0, 0), 0)),
214        );
215        assert_eq!(
216            parse(&mut &b"PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"[..]),
217            Ok(valid_v4(
218                (255, 255, 255, 255),
219                65535,
220                (255, 255, 255, 255),
221                65535,
222            )),
223        );
224
225        fn valid_v6(
226            (a, b, c, d, e, f, g, h): (u16, u16, u16, u16, u16, u16, u16, u16),
227            i: u16,
228            (j, k, l, m, n, o, p, q): (u16, u16, u16, u16, u16, u16, u16, u16),
229            r: u16,
230        ) -> ProxyHeader {
231            ProxyHeader::Version1 {
232                addresses: version1::ProxyAddresses::Ipv6 {
233                    source: SocketAddrV6::new(Ipv6Addr::new(a, b, c, d, e, f, g, h), i, 0, 0),
234                    destination: SocketAddrV6::new(Ipv6Addr::new(j, k, l, m, n, o, p, q), r, 0, 0),
235                },
236            }
237        }
238        assert_eq!(
239            parse(&mut &b"PROXY TCP6 ab:ce:ef:01:23:45:67:89 ::1 0 65535\r\n"[..]),
240            Ok(valid_v6(
241                (0xAB, 0xCE, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89),
242                0,
243                (0, 0, 0, 0, 0, 0, 0, 1),
244                65535,
245            )),
246        );
247        assert_eq!(
248            parse(&mut &b"PROXY TCP6 :: :: 0 0\r\n"[..]),
249            Ok(valid_v6(
250                (0, 0, 0, 0, 0, 0, 0, 0),
251                0,
252                (0, 0, 0, 0, 0, 0, 0, 0),
253                0,
254            )),
255        );
256        assert_eq!(
257            parse(
258                &mut &b"PROXY TCP6 ff:ff:ff:ff:ff:ff:ff:ff ff:ff:ff:ff:ff:ff:ff:ff 65535 65535\r\n"
259                    [..],
260            ),
261            Ok(valid_v6(
262                (0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF),
263                65535,
264                (0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF),
265                65535,
266            )),
267        );
268
269        assert_eq!(
270            parse(&mut &b"PROXY UNKNOWN \r"[..]),
271            Err(ParseError::Version1 {
272                source: version1::ParseError::UnexpectedEof,
273            }),
274        );
275        assert_eq!(
276            parse(&mut &b"PROXY UNKNOWN \r\t\t\r"[..]),
277            Err(ParseError::Version1 {
278                source: version1::ParseError::UnexpectedEof,
279            }),
280        );
281        assert_eq!(
282            parse(&mut &b"PROXY UNKNOWN\r\r\r\r\rHello, world!"[..]),
283            Err(ParseError::Version1 {
284                source: version1::ParseError::UnexpectedEof,
285            }),
286        );
287        assert_eq!(
288            parse(&mut &b"PROXY UNKNOWN\nGET /index.html HTTP/1.0"[..]),
289            Err(ParseError::Version1 {
290                source: version1::ParseError::UnexpectedEof,
291            }),
292        );
293        assert_eq!(
294            parse(&mut &b"PROXY UNKNOWN\n"[..]),
295            Err(ParseError::Version1 {
296                source: version1::ParseError::UnexpectedEof,
297            }),
298        );
299    }
300
301    #[test]
302    fn test_version2() {
303        const PREFIX_LOCAL: [u8; 13] = [
304            0x0D,
305            0x0A,
306            0x0D,
307            0x0A,
308            0x00,
309            0x0D,
310            0x0A,
311            0x51,
312            0x55,
313            0x49,
314            0x54,
315            0x0A,
316            (2 << 4) | 0,
317        ];
318        const PREFIX_PROXY: [u8; 13] = [
319            0x0D,
320            0x0A,
321            0x0D,
322            0x0A,
323            0x00,
324            0x0D,
325            0x0A,
326            0x51,
327            0x55,
328            0x49,
329            0x54,
330            0x0A,
331            (2 << 4) | 1,
332        ];
333
334        assert_eq!(
335            parse(&mut [&PREFIX_LOCAL[..], &[0u8; 16][..]].concat().as_slice()),
336            Ok(ProxyHeader::Version2 {
337                command: version2::ProxyCommand::Local,
338                addresses: version2::ProxyAddresses::Unspec,
339                transport_protocol: version2::ProxyTransportProtocol::Unspec,
340            }),
341        );
342        assert_eq!(
343            parse(&mut [&PREFIX_PROXY[..], &[0u8; 16][..]].concat().as_slice()),
344            Ok(ProxyHeader::Version2 {
345                command: version2::ProxyCommand::Proxy,
346                addresses: version2::ProxyAddresses::Unspec,
347                transport_protocol: version2::ProxyTransportProtocol::Unspec,
348            }),
349        );
350
351        assert_eq!(
352            parse(
353                &mut [
354                    &PREFIX_PROXY[..],
355                    &[
356                        // Inet << 4 | Stream
357                        (1 << 4) | 1,
358                        // Length beyond this: 12
359                        // Let's throw in a TLV with no data; 3 bytes.
360                        0,
361                        15,
362                        // Source IP
363                        127,
364                        0,
365                        0,
366                        1,
367                        // Destination IP
368                        192,
369                        168,
370                        0,
371                        1,
372                        // Source port
373                        // 65535 = [255, 255]
374                        255,
375                        255,
376                        // Destination port
377                        // 257 = [1, 1]
378                        1,
379                        1,
380                        // TLV
381                        69,
382                        0,
383                        0,
384                    ][..]
385                ]
386                .concat()
387                .as_slice(),
388            ),
389            Ok(ProxyHeader::Version2 {
390                command: version2::ProxyCommand::Proxy,
391                transport_protocol: version2::ProxyTransportProtocol::Stream,
392                addresses: version2::ProxyAddresses::Ipv4 {
393                    source: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 65535),
394                    destination: SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 257),
395                },
396            })
397        );
398
399        let mut data = Bytes::from(
400            [
401                &PREFIX_LOCAL[..],
402                &[
403                    // Inet << 4 | Datagram
404                    (1 << 4) | 2,
405                    // Length beyond this: 12
406                    0,
407                    12,
408                    // Source IP
409                    0,
410                    0,
411                    0,
412                    0,
413                    // Destination IP
414                    255,
415                    255,
416                    255,
417                    255,
418                    // Source port
419                    0,
420                    0,
421                    // Destination port
422                    255,
423                    0,
424                    // Extra data
425                    1,
426                    2,
427                    3,
428                    4,
429                ][..],
430            ]
431            .concat(),
432        );
433        assert_eq!(
434            parse(&mut data),
435            Ok(ProxyHeader::Version2 {
436                command: version2::ProxyCommand::Local,
437                transport_protocol: version2::ProxyTransportProtocol::Datagram,
438                addresses: version2::ProxyAddresses::Ipv4 {
439                    source: SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0),
440                    destination: SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), 255 << 8),
441                },
442            })
443        );
444        assert!(data.remaining() == 4); // Consume the entire header
445
446        assert_eq!(
447            parse(
448                &mut [
449                    &PREFIX_PROXY[..],
450                    &[
451                        // Inet6 << 4 | Datagram
452                        (2 << 4) | 2,
453                        // Length beyond this: 12
454                        // Let's throw in a TLV with no data; 3 bytes.
455                        0,
456                        39,
457                        // Source IP
458                        255,
459                        255,
460                        255,
461                        255,
462                        255,
463                        255,
464                        255,
465                        255,
466                        255,
467                        255,
468                        255,
469                        255,
470                        255,
471                        255,
472                        255,
473                        255,
474                        // Destination IP
475                        0,
476                        0,
477                        0,
478                        0,
479                        0,
480                        0,
481                        0,
482                        0,
483                        0,
484                        0,
485                        0,
486                        0,
487                        0,
488                        0,
489                        0,
490                        0,
491                        // Source port
492                        // 65535 = [255, 255]
493                        255,
494                        255,
495                        // Destination port
496                        // 257 = [1, 1]
497                        1,
498                        1,
499                        // TLV
500                        69,
501                        0,
502                        0,
503                    ][..],
504                ]
505                .concat()
506                .as_slice(),
507            ),
508            Ok(ProxyHeader::Version2 {
509                command: version2::ProxyCommand::Proxy,
510                transport_protocol: version2::ProxyTransportProtocol::Datagram,
511                addresses: version2::ProxyAddresses::Ipv6 {
512                    source: SocketAddrV6::new(
513                        Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535),
514                        65535,
515                        0,
516                        0,
517                    ),
518                    destination: SocketAddrV6::new(
519                        Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
520                        257,
521                        0,
522                        0,
523                    )
524                },
525            })
526        );
527
528        let mut data = Bytes::from(
529            [
530                &PREFIX_LOCAL[..],
531                &[
532                    // Inet6 << 4 | Stream
533                    (2 << 4) | 1,
534                    // Length beyond this: 12
535                    0,
536                    36,
537                    // Source IP
538                    81,
539                    92,
540                    0,
541                    52,
542                    83,
543                    12,
544                    255,
545                    68,
546                    19,
547                    5,
548                    111,
549                    200,
550                    54,
551                    90,
552                    55,
553                    66,
554                    // Destination IP
555                    255,
556                    255,
557                    255,
558                    255,
559                    0,
560                    0,
561                    0,
562                    0,
563                    123,
564                    123,
565                    69,
566                    69,
567                    21,
568                    21,
569                    42,
570                    42,
571                    // Source port
572                    123,
573                    0,
574                    // Destination port
575                    255,
576                    255,
577                    // Extra data
578                    1,
579                    2,
580                    3,
581                    4,
582                ][..],
583            ]
584            .concat(),
585        );
586        assert_eq!(
587            parse(&mut data),
588            Ok(ProxyHeader::Version2 {
589                command: version2::ProxyCommand::Local,
590                transport_protocol: version2::ProxyTransportProtocol::Stream,
591                addresses: version2::ProxyAddresses::Ipv6 {
592                    source: SocketAddrV6::new(
593                        Ipv6Addr::new(20828, 52, 21260, 65348, 4869, 28616, 13914, 14146),
594                        31488,
595                        0,
596                        0,
597                    ),
598                    destination: SocketAddrV6::new(
599                        Ipv6Addr::new(65535, 65535, 0, 0, 31611, 17733, 5397, 10794),
600                        65535,
601                        0,
602                        0,
603                    ),
604                },
605            })
606        );
607        assert!(data.remaining() == 4); // Consume the entire header
608
609        let mut data = [0u8; 200];
610        rand::thread_rng().fill_bytes(&mut data);
611        data[0] = 99; // Make 100% sure it's invalid.
612        assert!(parse(&mut &data[..]).is_err());
613
614        assert_eq!(
615            parse(&mut &PREFIX_LOCAL[..]),
616            Err(ParseError::Version2 {
617                source: version2::ParseError::UnexpectedEof,
618            }),
619        );
620
621        assert_eq!(
622            parse(
623                &mut [
624                    &PREFIX_PROXY[..],
625                    &[
626                        // Inet << 4 | Stream
627                        (1 << 4) | 1,
628                        // Length beyond this: 12
629                        // 3 bytes is clearly too few if we expect 2 IPv4s and ports
630                        0,
631                        3,
632                    ][..],
633                ]
634                .concat()
635                .as_slice(),
636            ),
637            Err(ParseError::Version2 {
638                source: version2::ParseError::InsufficientLengthSpecified {
639                    given: 3,
640                    needs: 4 * 2 + 2 * 2,
641                },
642            }),
643        );
644    }
645
646    #[test]
647    fn test_unknown_version() {
648        assert_eq!(
649            parse_version(
650                &mut &[
651                    0x0D,
652                    0x0A,
653                    0x0D,
654                    0x0A,
655                    0x00,
656                    0x0D,
657                    0x0A,
658                    0x51,
659                    0x55,
660                    0x49,
661                    0x54,
662                    0x0A,
663                    1 << 4, // Version goes in upper half of the byte
664                ][..],
665            ),
666            Err(ParseError::InvalidVersion { version: 1 }),
667        );
668    }
669
670    #[test]
671    fn test_version_parsing_correct() {
672        assert_eq!(
673            parse_version(&mut &[b'P', b'R', b'O', b'X', b'Y', b' '][..]),
674            Ok(1),
675        );
676        assert_eq!(
677            parse_version(
678                &mut &[
679                    0x0D,
680                    0x0A,
681                    0x0D,
682                    0x0A,
683                    0x00,
684                    0x0D,
685                    0x0A,
686                    0x51,
687                    0x55,
688                    0x49,
689                    0x54,
690                    0x0A,
691                    15 << 4, // Version goes in upper half of the byte
692                ][..],
693            ),
694            Ok(15),
695        );
696    }
697
698    #[test]
699    fn test_version_parsing_errors() {
700        assert_eq!(
701            parse_version(&mut &b"Proximyst"[..]),
702            Err(ParseError::NotProxyHeader)
703        );
704    }
705}