haprox_rs/protocol/
protocol_composer.rs

1/*-
2 * haprox-rs - a HaProxy protocol parser.
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * The scram-rs crate can be redistributed and/or modified
6 * under the terms of either of the following licenses:
7 *
8 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
9 *                     
10 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
11 */
12
13use std::{borrow::Cow, io::{Cursor, Write}, marker::PhantomData, mem::offset_of};
14
15use byteorder::{BigEndian, WriteBytesExt};
16use crc32fast::Hasher;
17
18use crate::{common, error::{HaProxErr, HaProxRes}, return_error};
19
20use super::{protocol::{self, HdrV2Command, PP2TlvClient, PP2Tlvs, ProxyTransportFam, ProxyV2Addr}, protocol_raw, PP2TlvDump, PP2TlvUniqId, ProxyV2OpCode};
21
22
23
24/// A quote from original protocol documentation.
25/// > the connection was established on purpose by the proxy
26/// > without being relayed. The connection endpoints are the sender and the
27/// > receiver. Such connections exist when the proxy sends health-checks to the
28/// > server. The receiver must accept this connection as valid and must use the
29/// > real connection endpoints and discard the protocol block including the
30/// > family which is ignored.
31#[derive(Clone, Debug)]
32pub struct HdrV2OpLocal;
33
34/// A quote from original protocol documentation.
35/// > the connection was established on behalf of another node,
36/// > and reflects the original connection endpoints. The receiver must then use
37/// > the information provided in the protocol block to get original the address.
38#[derive(Clone, Debug)]
39pub struct HdrV2OpProxy;
40
41impl ProxyV2OpCode for HdrV2OpLocal
42{
43    /// A 0x00 opcode.
44    const OPCODE: u8 = HdrV2Command::LOCAL as u8;
45}
46
47impl ProxyV2OpCode for HdrV2OpProxy
48{
49    /// A 0x01 opcode.
50    const OPCODE: u8 = HdrV2Command::PROXY as u8;
51}
52
53/// A SSL TLV composer.
54#[derive(Debug)]
55pub struct TlvSubTypeSsl<'s>
56{
57    /// Begining of the SSL TLV block.
58    start: u64,
59
60    /// A current [TlType] consumed from the root.
61    hdr: TlType<'s>,
62
63    /// A TLV ID constraints.
64    constraints: &'static [std::ops::RangeInclusive<u8>]
65}
66
67impl<'s> TlvSubTypeSsl<'s>
68{
69    fn new(mut main_tp: TlType<'s>, constraints: &'static [std::ops::RangeInclusive<u8>],
70        client: PP2TlvClient, verify: u32) -> HaProxRes<Self>
71    {
72        let start = main_tp.hdr.buffer.position();
73
74        main_tp.add_tlv(PP2Tlvs::TypeSsl{client, verify}, None)?;
75
76        return Ok(
77            Self
78            {
79                start: start,
80                hdr: main_tp,
81                constraints: constraints
82            }
83        );
84    }
85
86    #[inline]
87    fn done_int(&mut self) -> HaProxRes<()>
88    {
89        let cur_pos = self.hdr.hdr.buffer.position();
90
91        self.hdr.hdr.buffer.set_position(self.start + offset_of!(protocol_raw::PP2Tlv, length_hi) as u64);//1);
92
93        self.hdr.hdr.buffer.write_u16::<BigEndian>((cur_pos - self.start - 3) as u16).map_err(common::map_io_err)?;
94
95        self.hdr.hdr.buffer.set_position(cur_pos);
96
97        return Ok(());
98    }
99
100    /// When all necessary SSL sub TLVs are added, this function calculates the 
101    /// length of SSL TLV and writes the size.
102    /// 
103    /// # Returns
104    /// 
105    /// A [HaProxRes] is returned. On success the previous (root) [TlType] (which
106    /// was consumed previously) will be returned.
107    #[inline]
108    pub 
109    fn done(mut self) -> HaProxRes<TlType<'s>>
110    {
111        self.done_int()?;
112        
113
114        return Ok(self.hdr);
115    }
116
117    /// Sets the version of the SSL i,e TLSv1.2.
118    pub 
119    fn add_ssl_sub_version(&mut self, ver: impl Into<String>) -> HaProxRes<()>
120    {
121        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslVersion(Cow::Owned(ver.into())), Some(self.constraints));
122    }
123
124    /// Sets the Common Name field.
125    pub 
126    fn add_ssl_sub_cn(&mut self, cn: impl Into<String>) -> HaProxRes<()>
127    {
128        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCn(Cow::Owned(cn.into())), Some(self.constraints));
129    }
130
131    /// Sets the Cipher type i.e "ECDHE-RSA-AES128-GCM-SHA256"
132    pub 
133    fn add_ssl_sub_cipher(&mut self, ver: impl Into<String>) -> HaProxRes<()>
134    {
135        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCipher(Cow::Owned(ver.into())), Some(self.constraints));
136    }
137    
138    /// Sets the signature algorithm i.e "SHA256"
139    pub 
140    fn add_ssl_sub_sigalg(&mut self, ver: impl Into<String>) -> HaProxRes<()>
141    {
142        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslSigAlg(Cow::Owned(ver.into())), Some(self.constraints));
143    }
144
145    /// Sets the key algorithm i.e "RSA2048"
146    pub 
147    fn add_ssl_sub_keyalg(&mut self, ver: impl Into<String>) -> HaProxRes<()>
148    {
149        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslKeyAlg(Cow::Owned(ver.into())), Some(self.constraints));
150    }
151
152    /// Sets the namespace's name.
153    pub 
154    fn add_ssl_sub_netns(&mut self, ver: impl Into<String>) -> HaProxRes<()>
155    {
156        return self.hdr.add_tlv(PP2Tlvs::TypeNetNs(ver.into()), Some(self.constraints));
157    }
158}
159
160/// A root (main) TLV writer.
161#[derive(Debug)]
162pub struct TlType<'s>
163{
164    /// A reference to the header.
165    hdr: &'s mut ProxyHdrV2<HdrV2OpProxy>,
166
167    /// A TLVs which can be set on root level.
168    constraints: &'static [std::ops::RangeInclusive<u8>]
169}
170
171impl<'s> TlType<'s>
172{
173    fn new(hdr: &'s mut ProxyHdrV2<HdrV2OpProxy>, constraints: &'static [std::ops::RangeInclusive<u8>]) -> Self
174    {
175        return
176            Self
177            {
178                hdr,
179                constraints
180            };
181    }
182
183    /// Adds the Application-Layer Protocol Negotiation (ALPN)
184    pub 
185    fn add_alpn<'a>(&mut self, alpns: impl Iterator<Item = &'a [u8]>) -> HaProxRes<()>
186    {
187        return self.add_tlv(PP2Tlvs::TypeAlpn( alpns.map(|v| v.to_vec()).collect()), None);
188    }
189
190    /// Adds padding 3 bytes.
191    pub 
192    fn add_noop(&mut self) -> HaProxRes<()>
193    {
194        return self.add_tlv(PP2Tlvs::TypeNoop, None);
195    }
196
197    /// Adds  US-ASCII string representation of the namespace's name
198    pub 
199    fn add_netns(&mut self, ns: impl Into<String>) -> HaProxRes<()>
200    {
201        return self.add_tlv(PP2Tlvs::TypeNetNs(ns.into()), None);
202    }
203
204    /// Adds a 32-bit number storing the CRC32c checksum of the PROXY protocol header
205    /// 
206    /// It will be calculated after finalization.
207    pub 
208    fn add_crc32(&mut self) -> HaProxRes<()>
209    {
210        return self.add_tlv(PP2Tlvs::TypeCrc32c(0), None);
211    }
212
213    /// Adds a uniqID. See [PP2TlvUniqId]. 128 bytes max.
214    pub 
215    fn add_uniq_id<ID: PP2TlvUniqId>(&mut self, au: ID) -> HaProxRes<()>
216    {
217        let uniq_id = au.into_bytes();
218
219        return self.add_tlv(PP2Tlvs::TypeUniqId(uniq_id), None);
220    }
221
222    /// Adds "SNI" i.e the "server_name" extension as defined by RFC3546.
223    pub 
224    fn add_authority(&mut self, authority: impl Into<String>) -> HaProxRes<()>
225    {
226        return self.add_tlv(PP2Tlvs::TypeAuthority(authority.into()), None);
227    }
228
229    // --- SSL and subtypes ---
230
231    /// Adds SSL properties.
232    pub 
233    fn add_ssl(self, client: PP2TlvClient, verify: u32) -> HaProxRes<TlvSubTypeSsl<'s>>
234    {
235        return TlvSubTypeSsl::new(self, PP2Tlvs::TLV_TYPE_SSL_SUB_RANGE, client, verify);
236    }
237
238    /// Adding external TLV. The adding instance should implement [PP2TlvDump].
239    pub 
240    fn add_tlv<TLV: PP2TlvDump>(&mut self, tlv: TLV, 
241        opt_constr: Option<&'static [std::ops::RangeInclusive<u8>]>) -> HaProxRes<()>
242    {
243        let tlv_id: u8 = tlv.get_type();
244
245        let constr = opt_constr.unwrap_or(self.constraints);
246
247        if constr.iter().any(|idr| idr.contains(&tlv_id)) == false
248        {
249            return_error!(ArgumentEinval, "TLV: {} is subtype of other type or type!", tlv);
250        }
251
252        if tlv_id == PP2Tlvs::TypeCrc32c(0).into()
253        {
254            if self.hdr.crc_tlv_offset != 0
255            {
256                return_error!(ArgumentEinval, "diplicate PLT CRC!");
257            }
258
259            self.hdr.crc_tlv_offset = self.hdr.buffer.position();
260        }
261
262        // write type
263        self.hdr.buffer.write_u8(tlv_id).map_err(common::map_io_err)?;
264
265        // store size_position
266        let size_pos = self.hdr.buffer.position();
267
268        // write fake size
269        self.hdr.buffer.write_u16::<BigEndian>(0).map_err(common::map_io_err)?;   
270
271        // write TLV's content
272        tlv.dump(&mut self.hdr.buffer)?;
273
274        // write size
275        let cur_pos = self.hdr.buffer.position();
276        self.hdr.buffer.set_position(size_pos);
277
278        // size
279        self.hdr.buffer.write_u16::<BigEndian>((cur_pos - size_pos - 2) as u16).map_err(common::map_io_err)?;
280
281        // restore cursor
282        self.hdr.buffer.set_position(cur_pos);
283
284        return Ok(());
285    }
286}
287
288/// A Proxy Network packet composer.
289/// 
290/// Depending on the type of the generic `OPC` a different composing options
291/// are available.
292/// 
293/// OPC:
294/// 
295/// * `HdrV2OpLocal` - a local operation as described in the proxy documentation.
296/// 
297/// * `HdrV2OpProxy` - a proxy operation as described in the proxy documentation.
298/// 
299/// # Arguments
300/// 
301/// * `OPC` - [ProxyV2OpCode] a proxy opcode. It is intentionally made as a generic
302///     to separate the options which are availabe for each type of HaProxy message.
303/// 
304#[derive(Clone, Debug)]
305pub struct ProxyHdrV2<OPC: ProxyV2OpCode>
306{
307    /// Preallocard space for header.
308    buffer: Cursor<Vec<u8>>,
309
310    /// Offset to the location of the TLV CRC
311    crc_tlv_offset: u64,
312
313    /// Phantom for the [ProxyV2OpCode]
314    _p: PhantomData<OPC>,
315}
316
317impl<OPC: ProxyV2OpCode> ProxyHdrV2<OPC>
318{
319    /// Offset to the message length field.
320    pub const HDR_MSG_LEN_OFFSET: u64 = offset_of!(protocol_raw::ProxyHdrV2, len) as u64;
321}
322
323/// All functionality available for the opcode LOCAL.
324impl ProxyHdrV2<HdrV2OpLocal>
325{
326    /// Creates a new message of type LOCAL [HdrV2Command::LOCAL].
327    pub 
328    fn new() -> Vec<u8>
329    {
330        return protocol_raw::MSG_HEADER_LOCAL_V2.to_vec();
331    }
332}
333
334/// All functionality available for the opcode PROXY.
335impl ProxyHdrV2<HdrV2OpProxy>
336{
337    /// Creates new message of type PROXY [HdrV2Command::PROXY]. A program should provide the
338    /// type of the transport and address of the source and destination. The message compose instance 
339    /// is returned which already contains a prepared header.
340    /// 
341    /// # Arguments
342    /// 
343    /// * `transport` - [ProxyTransportFam] a type of the transport.
344    /// 
345    /// * `address` - a source and destination address encapsulated in [ProxyV2Addr].
346    /// 
347    /// # Returns
348    /// 
349    /// A [HaProxRes] is returned:
350    /// 
351    /// * [Result::Ok] - with the instance
352    /// 
353    /// * [Result::Err] - error description
354    pub 
355    fn new(transport: ProxyTransportFam, address: ProxyV2Addr) -> HaProxRes<Self>
356    {
357        let buf: Vec<u8> = 
358            Vec::with_capacity(size_of::<protocol_raw::ProxyHdrV2>());
359
360        let mut cur = Cursor::new(buf);
361        // writing header
362
363        // header_magic
364        cur.write_all(protocol_raw::HEADER_MAGIC_V2).map_err(common::map_io_err)?;
365
366        // protocol version and command 
367        cur.write_u8(0x20 | HdrV2OpProxy::OPCODE).map_err(common::map_io_err)?;
368
369        // addr type and transport
370        cur.write_u8(((address.as_addr_family() as u8) << 4) | transport as u8).map_err(common::map_io_err)?;
371
372        // length offset is known, so it will be updated later
373        cur.write_u16::<BigEndian>(address.get_len()).map_err(common::map_io_err)?;
374
375        // writing address
376        address.write(&mut cur)?;
377
378        return Ok(
379            Self
380            {
381                buffer: cur,
382                crc_tlv_offset: 0,
383                _p: PhantomData
384            }
385        );
386    }
387
388    /// Provides functionality to add the TLV to the payload of the message.
389    pub 
390    fn set_plts<'s>(&'s mut self) -> TlType<'s>
391    {
392        return TlType::new(self, PP2Tlvs::TLV_TYPE_MAIN_RANGES);
393    }
394
395    fn finalize(&mut self) -> HaProxRes<()>
396    {
397        let last_off = self.buffer.position();
398
399        // set position to HEADER LENGTH
400        self.buffer.set_position(Self::HDR_MSG_LEN_OFFSET);
401
402        let tlv_len = last_off - size_of::<protocol_raw::ProxyHdrV2>() as u64;
403
404        self.buffer.write_u16::<BigEndian>(tlv_len as u16).map_err(common::map_io_err)?;
405
406        if self.crc_tlv_offset > 0
407        {
408            // init hasher
409            let mut hasher = Hasher::new();
410
411            // calculate crc32
412            hasher.update(self.buffer.get_ref());
413
414            // set position to start of the CRC's plt payload
415            self.buffer.set_position(self.crc_tlv_offset + protocol::TLV_HEADER_LEN as u64);
416
417            // write back CRC
418            self.buffer.write_u32::<BigEndian>(hasher.finalize()).map_err(common::map_io_err)?;
419        }
420        return Ok(());
421    }
422
423    
424}
425
426/// Converts the instance to the vector. The message will be finalized i.e
427/// length recalculated, CRC updated.
428impl TryFrom<ProxyHdrV2<HdrV2OpProxy>> for Vec<u8>
429{
430    type Error = HaProxErr;
431
432    fn try_from(mut value: ProxyHdrV2<HdrV2OpProxy>) -> Result<Self, Self::Error> 
433    {
434        value.finalize()?;
435
436        return Ok(value.buffer.into_inner());
437    }
438}
439
440impl PP2TlvDump for PP2Tlvs
441{
442    fn get_type(&self) -> u8 
443    {
444        return self.into();
445    }
446
447    fn dump(&self, cur: &mut Cursor<Vec<u8>>) -> HaProxRes<()> 
448    {
449        match self
450        {
451            PP2Tlvs::TypeAlpn(items) => 
452            {
453                // payload
454                for alpn in items.iter()//.map(|v| v.as_bytes())
455                {
456                    // length u16
457                    cur.write_u16::<BigEndian>(alpn.len() as u16).map_err(common::map_io_err)?;
458
459                    // alpn bytes
460                    cur.write_all(alpn).map_err(common::map_io_err)?;
461                }
462            },
463            PP2Tlvs::TypeAuthority(auth) => 
464            {
465                // authority
466                cur.write_all(auth.as_bytes()).map_err(common::map_io_err)?;
467            },
468            PP2Tlvs::TypeCrc32c(crc) => 
469            {
470                cur.write_u32::<BigEndian>(*crc).map_err(common::map_io_err)?;
471            },
472            PP2Tlvs::TypeNoop => 
473            {
474
475            },
476            PP2Tlvs::TypeUniqId(items) =>
477            {
478                cur.write_all(items.as_slice()).map_err(common::map_io_err)?;
479            },
480            PP2Tlvs::TypeSsl{client, verify} => 
481            {
482                // client
483                cur.write_u8(client.bits()).map_err(common::map_io_err)?;
484
485                // verify
486                cur.write_u32::<BigEndian>(*verify).map_err(common::map_io_err)?;
487            },
488            PP2Tlvs::TypeSubtypeSslVersion(v) => 
489            {
490                cur.write_all(v.as_bytes()).map_err(common::map_io_err)?;
491            },
492            PP2Tlvs::TypeSubtypeSslCn(cn) => 
493            {
494                cur.write_all(cn.as_bytes()).map_err(common::map_io_err)?;
495            },
496            PP2Tlvs::TypeSubtypeSslCipher(c) => 
497            {
498                cur.write_all(c.as_bytes()).map_err(common::map_io_err)?;
499            },
500            PP2Tlvs::TypeSubtypeSslSigAlg(sa) => 
501            {
502                cur.write_all(sa.as_bytes()).map_err(common::map_io_err)?;
503            },
504            PP2Tlvs::TypeSubtypeSslKeyAlg(ka) => 
505            {
506                cur.write_all(ka.as_bytes()).map_err(common::map_io_err)?;
507            },
508            PP2Tlvs::TypeNetNs(ns) => 
509            {
510                cur.write_all(ns.as_bytes()).map_err(common::map_io_err)?;
511            },
512        }
513
514        return Ok(());
515    }
516}
517
518#[cfg(test)]
519mod tests
520{
521    use std::{fmt, io::Cursor};
522
523    use byteorder::{BigEndian, WriteBytesExt};
524
525    use crate::{common::map_io_err, protocol::{protocol::{PP2TlvClient, PP2Tlvs}, PP2TlvDump}, HaProxRes, ProxyTransportFam, ProxyV2Addr};
526
527    use super::{HdrV2OpProxy, ProxyHdrV2};
528
529    #[test]
530    fn test_comp0()
531    {
532        let addr = ProxyV2Addr::try_from(("127.0.0.1:39754", "127.0.0.67:11883")).unwrap();
533        
534        let mut comp = 
535            ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
536
537        let plts = comp.set_plts();
538
539        let mut ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
540
541        ssl.add_ssl_sub_version("TLSv1.2").unwrap();
542        
543        ssl.done().unwrap();
544
545        let pkt: Vec<u8> = comp.try_into().unwrap();
546
547        let ctrl = 
548b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\x21\x11\x00\x1e\
549\x7f\x00\x00\x01\x7f\x00\x00\x43\x9b\x4a\x2e\x6b\x20\x00\x0f\x01\
550\x00\x00\x00\x00\x21\x00\x07\x54\x4c\x53\x76\x31\x2e\x32";
551
552        assert_eq!(pkt.as_slice(), ctrl.as_slice());
553    }
554
555    #[test]
556    fn test_comp1()
557    {
558        #[derive(Clone, Debug)]
559        pub enum ProxyV2Dummy2 
560        {
561            SomeTlvName(u32, u32),
562        }
563
564        impl fmt::Display for ProxyV2Dummy2
565        {
566            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
567            {
568                write!(f, "DUMMY external reader")
569            }
570        }
571
572        impl PP2TlvDump for ProxyV2Dummy2
573        {
574            fn get_type(&self) -> u8 
575            {
576                let Self::SomeTlvName(..) = self else { panic!("wrong") };
577
578                return 0xE0;
579            }
580
581            fn dump(&self, cur: &mut Cursor<Vec<u8>>) -> HaProxRes<()> 
582            {
583                match self
584                {
585                    Self::SomeTlvName(arg0, arg1) =>
586                    {
587                        cur.write_u32::<BigEndian>(*arg0).map_err(map_io_err)?;
588                        cur.write_u32::<BigEndian>(*arg1).map_err(map_io_err)?;
589                    }
590                }
591
592                return Ok(());
593            }
594        }
595
596
597
598        let addr = ProxyV2Addr::try_from(("127.0.0.1:39754", "127.0.0.67:11883")).unwrap();
599        
600        let mut comp = 
601            ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
602
603        let plts = comp.set_plts();
604
605        let mut ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
606
607        ssl.add_ssl_sub_version("TLSv1.2").unwrap();
608        
609        let mut plts = ssl.done().unwrap();
610
611
612        let cust_plt = ProxyV2Dummy2::SomeTlvName(0x01020304, 0x05060708);
613
614        plts.add_tlv(cust_plt, Some(&[0xE0..=0xE0])).unwrap();
615
616        drop(plts);
617
618        let pkt: Vec<u8> = comp.try_into().unwrap();
619
620        let ctrl = 
621b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\x21\x11\x00\x29\
622\x7f\x00\x00\x01\x7f\x00\x00\x43\x9b\x4a\x2e\x6b\x20\x00\x0f\x01\
623\x00\x00\x00\x00\x21\x00\x07\x54\x4c\x53\x76\x31\x2e\x32\xE0\x00\
624\x08\x01\x02\x03\x04\x05\x06\x07\x08";
625
626        assert_eq!(pkt.as_slice(), ctrl.as_slice());
627    }
628
629    #[test]
630    fn test_alpns()
631    {
632        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
633
634        let alpn = PP2Tlvs::TypeAlpn(vec![b"test".as_slice().to_vec()]);
635
636        alpn.dump(&mut cur).unwrap();
637
638       // let op: u8 = (&alpn).into();
639
640        let reference = b"\x00\x04test";
641
642        let generated = cur.into_inner();
643
644        assert_eq!(generated.as_slice(), reference.as_slice());
645    }
646
647    #[test]
648    fn test_authority()
649    {
650        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
651
652        let alpn = PP2Tlvs::TypeAuthority("tset".into());
653
654        alpn.dump(&mut cur).unwrap();
655
656        //let op: u8 = (&alpn).into();
657
658        let reference = b"tset";
659
660        let generated = cur.into_inner();
661
662        assert_eq!(generated.as_slice(), reference.as_slice());
663    }
664
665    #[test]
666    fn test_crc32()
667    {
668        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
669
670        let alpn = PP2Tlvs::TypeCrc32c(0xABCDEF01);
671
672        alpn.dump(&mut cur).unwrap();
673
674        //let op: u8 = (&alpn).into();
675
676        let reference = b"\xab\xcd\xef\x01";
677
678        let generated = cur.into_inner();
679
680        assert_eq!(generated.as_slice(), reference.as_slice());
681    }
682
683    #[test]
684    fn test_netns()
685    {
686        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
687
688        let alpn = PP2Tlvs::TypeNetNs("tstt".into());
689
690        alpn.dump(&mut cur).unwrap();
691
692        //let op: u8 = (&alpn).into();
693
694        let reference = b"tstt";
695
696        let generated = cur.into_inner();
697
698        assert_eq!(generated.as_slice(), reference.as_slice());
699    }
700
701    #[test]
702    fn test_noop()
703    {
704        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
705
706        let alpn = PP2Tlvs::TypeNoop;
707
708        alpn.dump(&mut cur).unwrap();
709
710        let reference = b"";
711
712        let generated = cur.into_inner();
713
714        assert_eq!(generated.as_slice(), reference.as_slice());
715    }
716
717    #[test]
718    fn test_ssl()
719    {
720        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
721
722        let alpn = PP2Tlvs::TypeSsl{ client: PP2TlvClient::PP2_CLIENT_SSL, verify: 0x00003210};
723
724        alpn.dump(&mut cur).unwrap();
725
726        //let op: u8 = (&alpn).into();
727
728        let reference = b"\x01\x00\x00\x32\x10";
729
730        let generated = cur.into_inner();
731
732        assert_eq!(generated.as_slice(), reference.as_slice());
733    }
734
735    #[test]
736    fn test_uniqid()
737    {
738        const ID: &'static [u8] = b"ABCD12345678901234567890";
739
740        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
741
742        let alpn = PP2Tlvs::TypeUniqId(ID.into());
743
744        alpn.dump(&mut cur).unwrap();
745
746        let reference = ID;
747
748        let generated = cur.into_inner();
749
750        assert_eq!(generated.as_slice(), reference);
751    }
752
753    #[test]
754    fn test_ssl_cipher()
755    {
756        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
757
758        let ciph = PP2Tlvs::TypeSubtypeSslCipher("ECDHE-RSA-AES128-GCM-SHA256".into());
759
760        ciph.dump(&mut cur).unwrap();
761
762       // let op: u8 = (&ciph).into();
763
764        let reference = b"ECDHE-RSA-AES128-GCM-SHA256";
765
766        let generated = cur.into_inner();
767
768        assert_eq!(generated.as_slice(), reference.as_slice());
769    }
770
771    #[test]
772    fn test_ssl_cn()
773    {
774        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
775
776        let cn = PP2Tlvs::TypeSubtypeSslCn("example.com".into());
777
778        cn.dump(&mut cur).unwrap();
779
780        //let op: u8 = (&cn).into();
781
782        let reference = b"example.com";
783
784        let generated = cur.into_inner();
785
786        assert_eq!(generated.as_slice(), reference.as_slice());
787    }
788
789    #[test]
790    fn test_ssl_keyalg()
791    {
792        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
793
794        let keyalg = PP2Tlvs::TypeSubtypeSslKeyAlg("RSA2048".into());
795
796        keyalg.dump(&mut cur).unwrap();
797
798       // let op: u8 = (&keyalg).into();
799
800        let reference = b"RSA2048";
801
802        let generated = cur.into_inner();
803
804        assert_eq!(generated.as_slice(), reference.as_slice());
805    }
806
807    #[test]
808    fn test_ssl_sigalg()
809    {
810        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
811
812        let sigalg = PP2Tlvs::TypeSubtypeSslSigAlg("SHA256".into());
813
814        sigalg.dump(&mut cur).unwrap();
815
816       // let op: u8 = (&sigalg).into();
817
818        let reference = b"SHA256";
819
820        let generated = cur.into_inner();
821
822        assert_eq!(generated.as_slice(), reference.as_slice());
823    }
824
825    #[test]
826    fn test_ssl_version()
827    {
828        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
829
830        let vers = PP2Tlvs::TypeSubtypeSslVersion("TLSv1_3".into());
831
832        vers.dump(&mut cur).unwrap();
833
834        //let op: u8 = (&vers).into();
835
836        let reference = b"TLSv1_3";
837
838        let generated = cur.into_inner();
839
840        assert_eq!(generated.as_slice(), reference.as_slice());
841    }
842}