proxy_protocol_codec/
v2.rs

1//! PROXY Protocol v2.
2
3#[cfg(any(feature = "feat-codec-decode", feature = "feat-codec-encode"))]
4pub mod codec;
5pub mod model;
6
7#[cfg(any(feature = "feat-codec-decode", feature = "feat-codec-encode"))]
8pub use codec::*;
9pub use model::{AddressPair, Command, ExtensionRef, ExtensionType, Protocol, HEADER_SIZE};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12/// The PROXY Protocol v2 header.
13///
14/// For encoding and decoding stuffs, please refer to [`codec::HeaderEncoder`]
15/// and [`codec::HeaderDecoder`].
16pub struct Header {
17    /// See [`Command`].
18    command: Command,
19
20    /// See [`Protocol`].
21    protocol: Protocol,
22
23    /// See [`AddressPair`].
24    address_pair: AddressPair,
25}
26
27impl Header {
28    /// The magic bytes that identify the PROXY Protocol v2 header.
29    pub const MAGIC: &'static [u8; 12] = b"\r\n\r\n\x00\r\nQUIT\n";
30
31    #[inline]
32    /// Creates a new `Header` with [`Command::Local`]
33    pub const fn new_local() -> Self {
34        Self {
35            command: Command::Local,
36            protocol: Protocol::Unspecified,
37            address_pair: AddressPair::Unspecified,
38        }
39    }
40
41    #[inline]
42    /// Creates a new `Header` with [`Command::Proxy`]
43    pub const fn new_proxy(protocol: Protocol, address_pair: AddressPair) -> Self {
44        Self {
45            command: Command::Proxy,
46            protocol,
47            address_pair,
48        }
49    }
50
51    #[inline]
52    /// Returns the command of the header.
53    pub const fn command(&self) -> &Command {
54        &self.command
55    }
56
57    #[inline]
58    /// Returns the protocol of the header.
59    pub const fn protocol(&self) -> &Protocol {
60        &self.protocol
61    }
62
63    #[inline]
64    /// Returns the address pair of the header.
65    pub const fn address_pair(&self) -> &AddressPair {
66        &self.address_pair
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use alloc::vec;
73
74    use super::*;
75
76    #[test]
77    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
78    fn encode_decode_header_no_extension_local() {
79        let header = Header::new_local();
80
81        _encode_decode_header_no_extension(header);
82    }
83
84    #[test]
85    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
86    fn encode_decode_header_no_extension_proxy_unspec_unspec() {
87        let header = Header::new_proxy(Protocol::Unspecified, AddressPair::Unspecified);
88
89        _encode_decode_header_no_extension(header);
90    }
91
92    #[test]
93    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
94    fn encode_decode_header_no_extension_proxy_stream_unspec() {
95        let header = Header::new_proxy(Protocol::Stream, AddressPair::Unspecified);
96
97        _encode_decode_header_no_extension(header);
98    }
99
100    #[test]
101    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
102    fn encode_decode_header_no_extension_proxy_dgram_unspec() {
103        let header = Header::new_proxy(Protocol::Dgram, AddressPair::Unspecified);
104
105        _encode_decode_header_no_extension(header);
106    }
107
108    #[test]
109    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
110    fn encode_decode_header_no_extension_proxy_unspec_inet() {
111        let header = Header::new_proxy(
112            Protocol::Unspecified,
113            AddressPair::Inet {
114                src_ip: "127.0.0.1".parse().unwrap(),
115                dst_ip: "127.0.0.2".parse().unwrap(),
116                src_port: 8080,
117                dst_port: 80,
118            },
119        );
120
121        _encode_decode_header_no_extension(header);
122    }
123
124    #[test]
125    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
126    fn encode_decode_header_no_extension_proxy_stream_inet() {
127        let header = Header::new_proxy(
128            Protocol::Stream,
129            AddressPair::Inet {
130                src_ip: "127.0.0.1".parse().unwrap(),
131                dst_ip: "127.0.0.2".parse().unwrap(),
132                src_port: 8080,
133                dst_port: 80,
134            },
135        );
136
137        _encode_decode_header_no_extension(header);
138    }
139
140    #[test]
141    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
142    fn encode_decode_header_no_extension_proxy_dgram_inet() {
143        let header = Header::new_proxy(
144            Protocol::Dgram,
145            AddressPair::Inet {
146                src_ip: "127.0.0.1".parse().unwrap(),
147                dst_ip: "127.0.0.2".parse().unwrap(),
148                src_port: 8080,
149                dst_port: 80,
150            },
151        );
152
153        _encode_decode_header_no_extension(header);
154    }
155
156    #[test]
157    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
158    fn encode_decode_header_no_extension_proxy_unspec_inet6() {
159        let header = Header::new_proxy(
160            Protocol::Unspecified,
161            AddressPair::Inet6 {
162                src_ip: "::1".parse().unwrap(),
163                dst_ip: "::2".parse().unwrap(),
164                src_port: 8080,
165                dst_port: 80,
166            },
167        );
168
169        _encode_decode_header_no_extension(header);
170    }
171
172    #[test]
173    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
174    fn encode_decode_header_no_extension_proxy_stream_inet6() {
175        let header = Header::new_proxy(
176            Protocol::Stream,
177            AddressPair::Inet6 {
178                src_ip: "::1".parse().unwrap(),
179                dst_ip: "::2".parse().unwrap(),
180                src_port: 8080,
181                dst_port: 80,
182            },
183        );
184
185        _encode_decode_header_no_extension(header);
186    }
187
188    #[test]
189    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
190    fn encode_decode_header_no_extension_proxy_dgram_inet6() {
191        let header = Header::new_proxy(
192            Protocol::Dgram,
193            AddressPair::Inet6 {
194                src_ip: "::1".parse().unwrap(),
195                dst_ip: "::2".parse().unwrap(),
196                src_port: 8080,
197                dst_port: 80,
198            },
199        );
200
201        _encode_decode_header_no_extension(header);
202    }
203
204    #[test]
205    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
206    fn encode_decode_header_no_extension_proxy_unspec_unix() {
207        let header = Header::new_proxy(
208            Protocol::Unspecified,
209            AddressPair::Unix {
210                src_addr: [b'S'; 108],
211                dst_addr: [b'D'; 108],
212            },
213        );
214
215        _encode_decode_header_no_extension(header);
216    }
217
218    #[test]
219    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
220    fn encode_decode_header_no_extension_proxy_stream_unix() {
221        let header = Header::new_proxy(
222            Protocol::Stream,
223            AddressPair::Unix {
224                src_addr: [b'S'; 108],
225                dst_addr: [b'D'; 108],
226            },
227        );
228
229        _encode_decode_header_no_extension(header);
230    }
231
232    #[test]
233    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
234    fn encode_decode_header_no_extension_proxy_dgram_unix() {
235        let header = Header::new_proxy(
236            Protocol::Dgram,
237            AddressPair::Unix {
238                src_addr: [b'S'; 108],
239                dst_addr: [b'D'; 108],
240            },
241        );
242
243        _encode_decode_header_no_extension(header);
244    }
245
246    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
247    fn _encode_decode_header_no_extension(header: Header) {
248        let encoded = codec::HeaderEncoder::encode(&header).finish().unwrap();
249
250        let decoded = codec::HeaderDecoder::decode(&encoded).unwrap();
251        if let codec::Decoded::Some(codec::DecodedHeader {
252            header: decoded_header,
253            extensions,
254        }) = decoded
255        {
256            assert_eq!(decoded_header, header);
257            let mut extensions_iter = extensions.into_iter();
258            assert!(extensions_iter.next().is_none());
259        } else {
260            panic!("Expected decoded header");
261        }
262    }
263
264    // Tests with extensions
265    #[test]
266    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
267    fn encode_decode_header_with_extension_local_alpn() {
268        let header = Header::new_local();
269        let alpn_data = b"http/1.1";
270
271        _encode_decode_header_with_extension(
272            header,
273            |encoder| encoder.write_ext_alpn(alpn_data),
274            |mut extensions| {
275                let ext = extensions.next().unwrap().unwrap();
276                assert_eq!(ext.typ().unwrap(), ExtensionType::ALPN);
277                assert_eq!(ext.payload(), alpn_data);
278                assert!(extensions.next().is_none());
279            },
280        );
281    }
282
283    #[test]
284    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
285    fn encode_decode_header_with_extension_proxy_authority() {
286        let header = Header::new_proxy(
287            Protocol::Stream,
288            AddressPair::Inet {
289                src_ip: "127.0.0.1".parse().unwrap(),
290                dst_ip: "127.0.0.2".parse().unwrap(),
291                src_port: 8080,
292                dst_port: 80,
293            },
294        );
295        let authority_data = b"example.com";
296
297        _encode_decode_header_with_extension(
298            header,
299            |encoder| encoder.write_ext_authority(authority_data),
300            |mut extensions| {
301                let ext = extensions.next().unwrap().unwrap();
302                assert_eq!(ext.typ().unwrap(), ExtensionType::Authority);
303                assert_eq!(ext.payload(), authority_data);
304                assert!(extensions.next().is_none());
305            },
306        );
307    }
308
309    #[test]
310    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
311    fn encode_decode_header_with_extension_proxy_unique_id() {
312        let header = Header::new_proxy(
313            Protocol::Stream,
314            AddressPair::Inet6 {
315                src_ip: "::1".parse().unwrap(),
316                dst_ip: "::2".parse().unwrap(),
317                src_port: 8080,
318                dst_port: 80,
319            },
320        );
321        let unique_id = b"unique-connection-id-12345";
322
323        _encode_decode_header_with_extension(
324            header,
325            |encoder| encoder.write_ext_unique_id(unique_id),
326            |mut extensions| {
327                let ext = extensions.next().unwrap().unwrap();
328                assert_eq!(ext.typ().unwrap(), ExtensionType::NetworkNamespace); // Note: write_ext_unique_id has a bug, it uses NetworkNamespace
329                assert_eq!(ext.payload(), unique_id);
330                assert!(extensions.next().is_none());
331            },
332        );
333    }
334
335    #[test]
336    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
337    fn encode_decode_header_with_extension_proxy_network_namespace() {
338        let header = Header::new_proxy(
339            Protocol::Dgram,
340            AddressPair::Unix {
341                src_addr: [b'S'; 108],
342                dst_addr: [b'D'; 108],
343            },
344        );
345        let netns_data = b"my-namespace";
346
347        _encode_decode_header_with_extension(
348            header,
349            |encoder| encoder.write_ext_network_namespace(netns_data),
350            |mut extensions| {
351                let ext = extensions.next().unwrap().unwrap();
352                assert_eq!(ext.typ().unwrap(), ExtensionType::NetworkNamespace);
353                assert_eq!(ext.payload(), netns_data);
354                assert!(extensions.next().is_none());
355            },
356        );
357    }
358
359    #[test]
360    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
361    fn encode_decode_header_with_extension_proxy_no_op() {
362        let header = Header::new_proxy(
363            Protocol::Stream,
364            AddressPair::Inet {
365                src_ip: "127.0.0.1".parse().unwrap(),
366                dst_ip: "127.0.0.2".parse().unwrap(),
367                src_port: 8080,
368                dst_port: 80,
369            },
370        );
371        let padding_size = 5u16;
372
373        _encode_decode_header_with_extension(
374            header,
375            |encoder| encoder.write_ext_no_op(padding_size),
376            |mut extensions| {
377                let ext = extensions.next().unwrap().unwrap();
378                assert_eq!(ext.typ().unwrap(), ExtensionType::NoOp);
379                assert_eq!(ext.payload().len(), padding_size as usize);
380                assert_eq!(ext.payload(), &vec![0u8; padding_size as usize]);
381                assert!(extensions.next().is_none());
382            },
383        );
384    }
385
386    #[test]
387    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
388    fn encode_decode_header_with_multiple_extensions() {
389        let header = Header::new_proxy(
390            Protocol::Stream,
391            AddressPair::Inet {
392                src_ip: "127.0.0.1".parse().unwrap(),
393                dst_ip: "127.0.0.2".parse().unwrap(),
394                src_port: 8080,
395                dst_port: 80,
396            },
397        );
398        let alpn_data = b"h2";
399        let authority_data = b"test.example.org";
400        let padding_size = 3u16;
401
402        let encoded = codec::HeaderEncoder::encode(&header)
403            .write_ext_alpn(alpn_data)
404            .unwrap()
405            .write_ext_authority(authority_data)
406            .unwrap()
407            .write_ext_no_op(padding_size)
408            .unwrap()
409            .finish()
410            .unwrap();
411
412        let decoded = codec::HeaderDecoder::decode(&encoded).unwrap();
413        if let codec::Decoded::Some(codec::DecodedHeader {
414            header: decoded_header,
415            extensions,
416        }) = decoded
417        {
418            assert_eq!(decoded_header, header);
419
420            let mut extensions_iter = extensions.into_iter();
421
422            // Check ALPN extension
423            let ext1 = extensions_iter.next().unwrap().unwrap();
424            assert_eq!(ext1.typ().unwrap(), ExtensionType::ALPN);
425            assert_eq!(ext1.payload(), alpn_data);
426
427            // Check Authority extension
428            let ext2 = extensions_iter.next().unwrap().unwrap();
429            assert_eq!(ext2.typ().unwrap(), ExtensionType::Authority);
430            assert_eq!(ext2.payload(), authority_data);
431
432            // Check NoOp extension
433            let ext3 = extensions_iter.next().unwrap().unwrap();
434            assert_eq!(ext3.typ().unwrap(), ExtensionType::NoOp);
435            assert_eq!(ext3.payload().len(), padding_size as usize);
436            assert_eq!(ext3.payload(), &vec![0u8; padding_size as usize]);
437
438            // No more extensions
439            assert!(extensions_iter.next().is_none());
440        } else {
441            panic!("Expected decoded header");
442        }
443    }
444
445    #[test]
446    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
447    fn encode_decode_header_with_custom_extension() {
448        let header = Header::new_local();
449        let custom_type = 0x80u8; // Custom extension type
450        let custom_data = b"custom-extension-data";
451
452        let extension = ExtensionRef::new_custom(custom_type, custom_data).unwrap();
453        let encoded = codec::HeaderEncoder::encode(&header)
454            .write_ext_custom(extension)
455            .finish()
456            .unwrap();
457
458        let decoded = codec::HeaderDecoder::decode(&encoded).unwrap();
459        if let codec::Decoded::Some(codec::DecodedHeader {
460            header: decoded_header,
461            extensions,
462        }) = decoded
463        {
464            assert_eq!(decoded_header, header);
465
466            let mut extensions_iter = extensions.into_iter();
467            let ext = extensions_iter.next().unwrap().unwrap();
468            assert_eq!(ext.typ().unwrap_err(), custom_type); // Unknown type returns Err with raw byte
469            assert_eq!(ext.payload(), custom_data);
470            assert!(extensions_iter.next().is_none());
471        } else {
472            panic!("Expected decoded header");
473        }
474    }
475
476    #[test]
477    #[cfg(all(
478        feature = "feat-codec-encode",
479        feature = "feat-codec-decode",
480        feature = "feat-codec-v2-crc32c"
481    ))]
482    fn encode_decode_header_with_crc32c() {
483        let header = Header::new_proxy(
484            Protocol::Stream,
485            AddressPair::Inet {
486                src_ip: "127.0.0.1".parse().unwrap(),
487                dst_ip: "127.0.0.2".parse().unwrap(),
488                src_port: 8080,
489                dst_port: 80,
490            },
491        );
492
493        let encoded = codec::HeaderEncoder::encode(&header).finish_with_crc32c().unwrap();
494
495        let decoded = codec::HeaderDecoder::decode(&encoded).unwrap();
496        if let codec::Decoded::Some(codec::DecodedHeader {
497            header: decoded_header,
498            extensions,
499        }) = decoded
500        {
501            assert_eq!(decoded_header, header);
502
503            let mut extensions_iter = extensions.into_iter();
504            let ext = extensions_iter.next().unwrap().unwrap();
505            assert_eq!(ext.typ().unwrap(), ExtensionType::CRC32C);
506            assert_eq!(ext.payload().len(), 4); // CRC32C is 4 bytes
507            assert!(extensions_iter.next().is_none());
508        } else {
509            panic!("Expected decoded header");
510        }
511    }
512
513    #[cfg(all(feature = "feat-codec-encode", feature = "feat-codec-decode"))]
514    fn _encode_decode_header_with_extension<F, V>(header: Header, add_extension: F, verify_extension: V)
515    where
516        F: FnOnce(
517            codec::HeaderEncoder<codec::encode::stage::Finished>,
518        ) -> Result<codec::HeaderEncoder<codec::encode::stage::Finished>, codec::EncodeError>,
519        V: FnOnce(codec::DecodedExtensionsIter<'_>),
520    {
521        let encoded = add_extension(codec::HeaderEncoder::encode(&header))
522            .unwrap()
523            .finish()
524            .unwrap();
525
526        let decoded = codec::HeaderDecoder::decode(&encoded).unwrap();
527        if let codec::Decoded::Some(codec::DecodedHeader {
528            header: decoded_header,
529            extensions,
530        }) = decoded
531        {
532            assert_eq!(decoded_header, header);
533            verify_extension(extensions.into_iter());
534        } else {
535            panic!("Expected decoded header");
536        }
537    }
538
539    #[test]
540    #[cfg(feature = "feat-codec-decode")]
541    fn decode_peek_insufficient_data() {
542        // Test with buffers of various sizes less than HEADER_SIZE (16 bytes)
543        // that start with magic bytes
544        for size in 1..16 {
545            let mut buffer = vec![0u8; size];
546            // Copy as much of the magic bytes as we can fit
547            let magic = b"\r\n\r\n\x00\r\nQUIT\n";
548            let copy_len = size.min(magic.len());
549            buffer[..copy_len].copy_from_slice(&magic[..copy_len]);
550
551            let result = codec::HeaderDecoder::decode(&buffer).unwrap();
552            // If we have the full magic bytes, it should be Partial
553            assert!(matches!(result, codec::Decoded::Partial(_)));
554        }
555
556        // Test with buffer that has no magic bytes
557        let buffer = vec![0u8; 8];
558        let result = codec::HeaderDecoder::decode(&buffer).unwrap();
559        assert!(matches!(result, codec::Decoded::None));
560    }
561
562    #[test]
563    #[cfg(feature = "feat-codec-decode")]
564    fn decode_peek_not_proxy_protocol() {
565        // Buffer that's large enough but doesn't start with magic bytes
566        let buffer = vec![0u8; 16];
567        let result = codec::HeaderDecoder::decode(&buffer).unwrap();
568        assert!(matches!(result, codec::Decoded::None));
569
570        // Buffer with wrong magic bytes
571        let mut buffer = vec![0u8; 16];
572        buffer[0..12].copy_from_slice(b"wrong_magic\x00");
573        let result = codec::HeaderDecoder::decode(&buffer).unwrap();
574        assert!(matches!(result, codec::Decoded::None));
575    }
576
577    #[test]
578    #[cfg(feature = "feat-codec-decode")]
579    fn decode_malformed_data() {
580        // Create a valid header first
581        let header = Header::new_proxy(
582            Protocol::Stream,
583            AddressPair::Inet {
584                src_ip: "127.0.0.1".parse().unwrap(),
585                dst_ip: "127.0.0.2".parse().unwrap(),
586                src_port: 8080,
587                dst_port: 80,
588            },
589        );
590        let encoded = codec::HeaderEncoder::encode(&header).finish().unwrap();
591
592        // Test with insufficient data for address parsing (truncate after header)
593        let truncated = &encoded[..16]; // Only header, no address data
594        let result = codec::HeaderDecoder::decode(truncated).unwrap();
595        assert!(matches!(result, codec::Decoded::Partial(_)));
596
597        // Test with partial address data
598        let partial = &encoded[..20]; // Header + partial IPv4 address
599        let result = codec::HeaderDecoder::decode(partial).unwrap();
600        assert!(matches!(result, codec::Decoded::Partial(_)));
601    }
602
603    #[test]
604    #[cfg(feature = "feat-codec-decode")]
605    fn decode_total_length_overflow() {
606        // Create a buffer with magic bytes and valid version/command
607        let mut buffer = vec![0u8; 16];
608        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
609        buffer[12] = 0x21; // Version 2, Command Proxy
610        buffer[13] = 0x11; // Family Inet, Protocol Stream
611
612        // Set length to maximum value that would cause overflow
613        buffer[14] = 0xFF;
614        buffer[15] = 0xFF;
615
616        // try_decode will return Partial requesting more data for the large length
617        let result = codec::HeaderDecoder::decode(&buffer).unwrap();
618        assert!(matches!(result, codec::Decoded::Partial(_)));
619    }
620
621    #[test]
622    #[cfg(feature = "feat-codec-decode")]
623    fn decode_invalid_version_variants() {
624        let mut buffer = vec![0u8; 16];
625        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
626        buffer[13] = 0x11; // Valid family and protocol
627        buffer[14] = 0x00; // Length hi
628        buffer[15] = 0x00; // Length lo
629
630        // Test various invalid version values
631        let invalid_versions = [
632            0x00, 0x10, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
633        ];
634        for &invalid_version in &invalid_versions {
635            buffer[12] = invalid_version | 0x01; // Keep command as Proxy
636            let err = codec::HeaderDecoder::decode(&buffer).unwrap_err();
637            assert!(matches!(err, codec::DecodeError::InvalidVersion(v) if v == invalid_version));
638        }
639    }
640
641    #[test]
642    #[cfg(feature = "feat-codec-decode")]
643    fn decode_invalid_command_variants() {
644        let mut buffer = vec![0u8; 16];
645        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
646        buffer[12] = 0x20; // Valid version 2
647        buffer[13] = 0x11; // Valid family and protocol
648        buffer[14] = 0x00; // Length hi
649        buffer[15] = 0x00; // Length lo
650
651        // Test various invalid command values
652        let invalid_commands = [
653            0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
654        ];
655        for &invalid_command in &invalid_commands {
656            buffer[12] = 0x20 | invalid_command;
657            let err = codec::HeaderDecoder::decode(&buffer).unwrap_err();
658            assert!(matches!(err, codec::DecodeError::InvalidCommand(c) if c == invalid_command));
659        }
660    }
661
662    #[test]
663    #[cfg(feature = "feat-codec-decode")]
664    fn decode_invalid_family_variants() {
665        let mut buffer = vec![0u8; 16];
666        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
667        buffer[12] = 0x21; // Valid version and command
668        buffer[14] = 0x00; // Length hi
669        buffer[15] = 0x00; // Length lo
670
671        // Test various invalid family values
672        let invalid_families = [0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0];
673        for &invalid_family in &invalid_families {
674            buffer[13] = invalid_family | 0x01; // Keep protocol as Stream
675            let err = codec::HeaderDecoder::decode(&buffer).unwrap_err();
676            assert!(matches!(err, codec::DecodeError::InvalidFamily(f) if f == invalid_family));
677        }
678    }
679
680    #[test]
681    #[cfg(feature = "feat-codec-decode")]
682    fn decode_invalid_protocol_variants() {
683        let mut buffer = vec![0u8; 16];
684        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
685        buffer[12] = 0x21; // Valid version and command
686        buffer[13] = 0x10; // Valid family Inet
687        buffer[14] = 0x00; // Length hi
688        buffer[15] = 0x00; // Length lo
689
690        // Test various invalid protocol values
691        let invalid_protocols = [
692            0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
693        ];
694        for &invalid_protocol in &invalid_protocols {
695            buffer[13] = 0x10 | invalid_protocol;
696            let err = codec::HeaderDecoder::decode(&buffer).unwrap_err();
697            assert!(matches!(err, codec::DecodeError::InvalidProtocol(p) if p == invalid_protocol));
698        }
699    }
700
701    #[test]
702    #[cfg(feature = "feat-codec-decode")]
703    fn decode_trailing_data_variants() {
704        // Create a minimal valid header (Local command)
705        let header = Header::new_local();
706        let encoded = codec::HeaderEncoder::encode(&header).finish().unwrap();
707
708        // Test with various amounts of trailing data
709        for extra_bytes in 1..=10 {
710            let mut with_trailing = encoded.clone();
711            with_trailing.extend(vec![0xAA; extra_bytes]);
712
713            // try_decode should detect trailing data and return an error
714            let err = codec::HeaderDecoder::decode(&with_trailing).unwrap_err();
715            assert!(matches!(err, codec::DecodeError::TrailingData));
716        }
717    }
718
719    #[test]
720    #[cfg(feature = "feat-codec-decode")]
721    fn decode_inet6_malformed() {
722        // Create header for IPv6 but with insufficient data
723        let mut buffer = vec![0u8; 16];
724        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
725        buffer[12] = 0x21; // Version 2, Command Proxy
726        buffer[13] = 0x21; // Family Inet6, Protocol Stream
727        buffer[14] = 0x00; // Length hi
728        buffer[15] = 0x24; // Length lo (36 bytes for IPv6 addresses)
729
730        // Create buffer with header but insufficient address data
731        let mut insufficient_data = buffer.clone();
732        insufficient_data.extend(vec![0u8; 20]); // Only 20 bytes instead of 36
733
734        let result = codec::HeaderDecoder::decode(&insufficient_data).unwrap();
735        assert!(matches!(result, codec::Decoded::Partial(_)));
736    }
737
738    #[test]
739    #[cfg(feature = "feat-codec-decode")]
740    fn decode_unix_malformed() {
741        // Create header for Unix but with insufficient data
742        let mut buffer = vec![0u8; 16];
743        buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
744        buffer[12] = 0x21; // Version 2, Command Proxy
745        buffer[13] = 0x31; // Family Unix, Protocol Stream
746        buffer[14] = 0x00; // Length hi
747        buffer[15] = 0xD8; // Length lo (216 bytes for Unix addresses)
748
749        // Create buffer with header but insufficient address data
750        let mut insufficient_data = buffer.clone();
751        insufficient_data.extend(vec![0u8; 100]); // Only 100 bytes instead of 216
752
753        let result = codec::HeaderDecoder::decode(&insufficient_data).unwrap();
754        assert!(matches!(result, codec::Decoded::Partial(_)));
755    }
756
757    #[test]
758    #[cfg(feature = "feat-codec-decode")]
759    fn decode_zero_length_but_with_address_family() {
760        // Test headers that claim to have address families but with zero length
761        let families_and_protocols = [
762            (0x10, 0x01), // Inet + Stream
763            (0x20, 0x01), // Inet6 + Stream
764            (0x30, 0x01), // Unix + Stream
765        ];
766
767        for (family, protocol) in families_and_protocols {
768            let mut buffer = vec![0u8; 16];
769            buffer[0..12].copy_from_slice(b"\r\n\r\n\x00\r\nQUIT\n");
770            buffer[12] = 0x21; // Version 2, Command Proxy
771            buffer[13] = family | protocol;
772            buffer[14] = 0x00; // Length hi - zero length
773            buffer[15] = 0x00; // Length lo - zero length
774
775            let err = codec::HeaderDecoder::decode(&buffer).unwrap_err();
776            assert!(matches!(err, codec::DecodeError::MalformedData));
777        }
778    }
779}