haprox_rs/protocol_v2/
protocol_composer.rs

1/*-
2 * haprox-rs - a HaProxy protocol parser.
3 * 
4 * Copyright 2025 (c) 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. The MIT License (MIT)
11 *                     
12 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
13 */
14
15use std::{borrow::{Cow}, io::{Cursor, Write}, marker::PhantomData, mem::offset_of};
16
17use byteorder::{BigEndian, WriteBytesExt};
18use crc32fast::Hasher;
19
20use crate::{common, protocol_raw, error::{HaProxErr, HaProxRes}, return_error};
21
22use super::
23{
24    protocol::
25    {
26        self, 
27        HdrV2Command, 
28        PP2TlvClient, 
29        PP2Tlvs, 
30        ProxyTransportFam, 
31        ProxyV2Addr
32    }, 
33    PP2TlvDump, 
34    PP2TlvUniqId, 
35    ProxyV2OpCode
36};
37
38/// A [ProxyV2OpCode] opcode which is used in [ProxyHdrV2]
39/// 
40/// A quote from original protocol documentation.
41/// > the connection was established on purpose by the proxy
42/// > without being relayed. The connection endpoints are the sender and the
43/// > receiver. Such connections exist when the proxy sends health-checks to the
44/// > server. The receiver must accept this connection as valid and must use the
45/// > real connection endpoints and discard the protocol block including the
46/// > family which is ignored.
47/// 
48/// ## Example
49/// 
50/// ```ignore
51/// let mut comp = 
52///     ProxyHdrV2::<HdrV2OpLocal>::new().unwrap();
53/// ```
54#[derive(Clone, Debug)]
55pub struct HdrV2OpLocal;
56
57/// A [ProxyV2OpCode] opcode which is used in [ProxyHdrV2]
58/// 
59/// A quote from original protocol documentation.
60/// > the connection was established on behalf of another node,
61/// > and reflects the original connection endpoints. The receiver must then use
62/// > the information provided in the protocol block to get original the address.
63/// 
64/// ## Example 
65/// 
66/// ```ignore
67/// let mut comp = 
68///     ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr)
69///         .unwrap();
70/// ```
71#[derive(Clone, Debug)]
72pub struct HdrV2OpProxy;
73
74impl ProxyV2OpCode for HdrV2OpLocal
75{
76    /// A 0x00 opcode.
77    const OPCODE: u8 = HdrV2Command::LOCAL as u8;
78}
79
80impl ProxyV2OpCode for HdrV2OpProxy
81{
82    /// A 0x01 opcode.
83    const OPCODE: u8 = HdrV2Command::PROXY as u8;
84}
85
86/// A SSL TLV composer.
87#[derive(Debug)]
88pub struct TlvSubTypeSsl<'s>
89{
90    /// Begining of the SSL TLV block.
91    start: u64,
92
93    /// A current [TlType] consumed from the root.
94    hdr: TlType<'s>,
95
96    /// A TLV ID constraints.
97    constraints: &'static [std::ops::RangeInclusive<u8>]
98}
99
100impl<'s> TlvSubTypeSsl<'s>
101{
102    fn new(mut main_tp: TlType<'s>, constraints: &'static [std::ops::RangeInclusive<u8>],
103        client: PP2TlvClient, verify: u32) -> HaProxRes<Self>
104    {
105        let start = main_tp.hdr.buffer.position();
106
107        main_tp.add_tlv(PP2Tlvs::TypeSsl{client, verify}, None)?;
108
109        return Ok(
110            Self
111            {
112                start: start,
113                hdr: main_tp,
114                constraints: constraints
115            }
116        );
117    }
118
119    #[inline]
120    fn done_int(&mut self) -> HaProxRes<()>
121    {
122        let cur_pos = self.hdr.hdr.buffer.position();
123
124        self.hdr.hdr.buffer.set_position(self.start + offset_of!(protocol_raw::PP2Tlv, length_hi) as u64);//1);
125
126        self.hdr.hdr.buffer.write_u16::<BigEndian>((cur_pos - self.start - 3) as u16).map_err(common::map_io_err)?;
127
128        self.hdr.hdr.buffer.set_position(cur_pos);
129
130        return Ok(());
131    }
132
133    /// When all necessary SSL sub TLVs are added, this function calculates the 
134    /// length of SSL TLV and writes the size.
135    /// 
136    /// # Returns
137    /// 
138    /// A [HaProxRes] is returned. On success the previous (root) [TlType] (which
139    /// was consumed previously) will be returned.
140    #[inline]
141    pub 
142    fn done(mut self) -> HaProxRes<TlType<'s>>
143    {
144        self.done_int()?;
145        
146
147        return Ok(self.hdr);
148    }
149
150    /// Sets the version of the SSL i,e TLSv1.2.
151    /// 
152    /// > US-ASCII string representation of the TLS version (format?)
153    /// 
154    /// # Returns
155    /// 
156    /// An error will be returned if `ver` contains non-ascii, non-printable and
157    /// whitespace.
158    /// 
159    /// Other error maybe returned too.
160    pub 
161    fn add_ssl_sub_version(&mut self, ver: impl Into<String>) -> HaProxRes<()>
162    {
163        let version = ver.into();
164
165        common::is_printable_ascii_nowp(&version, "version")?;
166
167        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslVersion(Cow::Owned(version)), Some(self.constraints));
168    }
169
170    /// Sets borrowed the version of the SSL i,e TLSv1.2.
171    /// 
172    /// > US-ASCII string representation of the TLS version (format?)
173    /// 
174    /// # Returns
175    /// 
176    /// An error will be returned if `ver` contains non-ascii, non-printable and
177    /// whitespace.
178    /// 
179    /// Other error maybe returned too.
180    pub 
181    fn add_ssl_sub_version_borrow<'a>(&mut self, ver: &'a str) -> HaProxRes<()>
182    {
183        common::is_printable_ascii_nowp(ver, "ssl_version")?;
184
185        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslVersion(Cow::Borrowed(ver)), Some(self.constraints));
186    }
187
188    /// Sets the Common Name field.
189    /// 
190    /// > In all cases, the string representation (in UTF8) of the Common Name field
191    /// > (OID: 2.5.4.3) of the client certificate's Distinguished Name, is appended
192    /// > using the TLV format and the type PP2_SUBTYPE_SSL_CN. E.g. "example.com".
193    /// 
194    /// Does not perform validation. ToDo?
195    pub 
196    fn add_ssl_sub_cn(&mut self, cn: impl Into<String>) -> HaProxRes<()>
197    {
198        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCn(Cow::Owned(cn.into())), Some(self.constraints));
199    }
200
201    /// Sets the borrowed Common Name field.
202    /// 
203    /// > In all cases, the string representation (in UTF8) of the Common Name field
204    /// > (OID: 2.5.4.3) of the client certificate's Distinguished Name, is appended
205    /// > using the TLV format and the type PP2_SUBTYPE_SSL_CN. E.g. "example.com".
206    /// 
207    /// Does not perform validation. ToDo?
208    pub 
209    fn add_ssl_sub_cn_borrow<'a>(&mut self, cn: &'a str) -> HaProxRes<()>
210    {
211        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCn(Cow::Borrowed(cn)), Some(self.constraints));
212    }
213
214    /// Sets the Cipher type i.e "ECDHE-RSA-AES128-GCM-SHA256"
215    /// 
216    /// > US-ASCII string name of the used cipher, for example "ECDHE-RSA-AES128-GCM-SHA256".
217    /// 
218    /// # Returns
219    /// 
220    /// An error will be returned if `ver` contains non-ascii, non-printable and
221    /// whitespace.
222    /// 
223    /// Other error maybe returned too.
224    pub 
225    fn add_ssl_sub_cipher(&mut self, cip: impl Into<String>) -> HaProxRes<()>
226    {
227        let cipher = cip.into();
228
229        common::is_printable_ascii_nowp(&cipher, "ssl_cipher")?;
230
231        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCipher(Cow::Owned(cipher)), Some(self.constraints));
232    }
233
234    /// Sets the borrowed Cipher type i.e "ECDHE-RSA-AES128-GCM-SHA256"
235    /// 
236    /// > US-ASCII string name of the used cipher, for example "ECDHE-RSA-AES128-GCM-SHA256".
237    /// 
238    /// # Returns
239    /// 
240    /// An error will be returned if `cip` contains non-ascii, non-printable and
241    /// whitespace.
242    /// 
243    /// Other error maybe returned too.
244    pub 
245    fn add_ssl_sub_cipher_borrow<'a>(&mut self, cip: &'a str) -> HaProxRes<()>
246    {
247        common::is_printable_ascii_nowp(cip, "ssl_cipher")?;
248
249        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslCipher(Cow::Borrowed(cip)), Some(self.constraints));
250    }
251    
252    /// Sets the signature algorithm i.e "SHA256"
253    /// 
254    /// > US-ASCII string name of the algorithm used to sign the certificate presented by the 
255    /// > frontend when the incoming connection was made over an SSL/TLS transport layer, for example
256    /// > "SHA256".
257    /// 
258    /// # Returns
259    /// 
260    /// An error will be returned if `sigalg` contains non-ascii, non-printable and
261    /// whitespace.
262    /// 
263    /// Other error maybe returned too.
264    pub 
265    fn add_ssl_sub_sigalg(&mut self, sigalg: impl Into<String>) -> HaProxRes<()>
266    {
267        let sig_alg = sigalg.into();
268
269        common::is_printable_ascii_nowp(&sig_alg, "sig_alg")?;
270
271        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslSigAlg(Cow::Owned(sig_alg)), Some(self.constraints));
272    }
273
274    /// Sets the signature algorithm i.e "SHA256"
275    /// 
276    /// > US-ASCII string name of the algorithm used to sign the certificate presented by the 
277    /// > frontend when the incoming connection was made over an SSL/TLS transport layer, for example
278    /// > "SHA256".
279    /// 
280    /// # Returns
281    /// 
282    /// An error will be returned if `sigalg` contains non-ascii, non-printable and
283    /// whitespace.
284    /// 
285    /// Other error maybe returned too.
286    pub 
287    fn add_ssl_sub_sigalg_borrow<'a>(&mut self, sigalg: &'a str) -> HaProxRes<()>
288    {
289        common::is_printable_ascii_nowp(sigalg, "ssl_sigalg")?;
290
291        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslSigAlg(Cow::Borrowed(sigalg)), Some(self.constraints));
292    }
293
294    /// Sets the key algorithm i.e "RSA2048"
295    /// 
296    /// > US-ASCII string name of the algorithm used to generate the key of the certificate 
297    /// > presented by the frontend when the incoming connection was made over an SSL/TLS 
298    /// > transport layer, for example "RSA2048".
299    /// 
300    /// # Returns
301    /// 
302    /// An error will be returned if `key` contains non-ascii, non-printable and
303    /// whitespace.
304    /// 
305    /// Other error maybe returned too.
306    pub 
307    fn add_ssl_sub_keyalg(&mut self, key: impl Into<String>) -> HaProxRes<()>
308    {
309        let key_alg = key.into();
310
311        common::is_printable_ascii_nowp(&key_alg, "sig_alg")?;
312
313        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslKeyAlg(Cow::Owned(key_alg)), Some(self.constraints));
314    }
315
316    /// Sets the borrowed key algorithm i.e "RSA2048"
317    /// 
318    /// > US-ASCII string name of the algorithm used to generate the key of the certificate 
319    /// > presented by the frontend when the incoming connection was made over an SSL/TLS 
320    /// > transport layer, for example "RSA2048".
321    /// 
322    /// # Returns
323    /// 
324    /// An error will be returned if `key` contains non-ascii, non-printable and
325    /// whitespace.
326    /// 
327    /// Other error maybe returned too.
328    pub 
329    fn add_ssl_sub_keyalg_borrow<'a>(&mut self, key: &'a str) -> HaProxRes<()>
330    {
331        common::is_printable_ascii_nowp(key, "ssl_sigalg")?;
332
333        return self.hdr.add_tlv(PP2Tlvs::TypeSubtypeSslKeyAlg(Cow::Borrowed(key)), Some(self.constraints));
334    }
335
336    /// Sets the ssl group.
337    /// 
338    /// > US-ASCII string name of the key exchange algorithm used for the frontend TLS 
339    /// > connection, for example "secp256r1".
340    /// 
341    /// # Returns
342    /// 
343    /// An error will be returned if `group` contains non-ascii, non-printable and
344    /// whitespace.
345    /// 
346    /// Other error maybe returned too.
347    pub 
348    fn add_ssl_group(&mut self, group: impl Into<String>) -> HaProxRes<()>
349    {
350        let group_str = group.into();
351
352        common::is_printable_ascii_nowp(&group_str, "ssl_group")?;
353
354        return self.hdr.add_tlv(PP2Tlvs::TypeSubTypeSslGroup(group_str.into()), Some(self.constraints));
355    }
356
357    /// Sets the borrowed ssl group.
358    /// 
359    /// > US-ASCII string name of the key exchange algorithm used for the frontend TLS 
360    /// > connection, for example "secp256r1".
361    /// 
362    /// # Returns
363    /// 
364    /// An error will be returned if `group` contains non-ascii, non-printable and
365    /// whitespace.
366    /// 
367    /// Other error maybe returned too.
368    pub 
369    fn add_ssl_group_group<'a>(&mut self, group: &'a str) -> HaProxRes<()>
370    {
371        common::is_printable_ascii_nowp(group, "ssl_group")?;
372
373        return self.hdr.add_tlv(PP2Tlvs::TypeSubTypeSslGroup(Cow::Borrowed(group)), Some(self.constraints));
374    }
375
376    /// Sets the SSL signature scheme.
377    /// 
378    /// > US-ASCII string name of the algorithm the frontend used to sign the ServerKeyExchange or
379    /// > CertificateVerify message, for example "rsa_pss_rsae_sha256".
380    /// 
381    /// # Returns
382    /// 
383    /// An error will be returned if `sig_schm` contains non-ascii, non-printable and
384    /// whitespace.
385    /// 
386    /// Other error maybe returned too.
387    pub 
388    fn add_ssl_sig_scheme(&mut self, sig_schm: impl Into<String>) -> HaProxRes<()>
389    {
390        let sig_schm_str = sig_schm.into();
391
392        common::is_printable_ascii_nowp(&sig_schm_str, "ssl_sig_schm")?;
393
394        return self.hdr.add_tlv(PP2Tlvs::TypeSubTypeSslSigScheme(sig_schm_str.into()), Some(self.constraints));
395    }
396
397    /// Sets the borrowed SSL signature scheme.
398    /// 
399    /// > US-ASCII string name of the algorithm the frontend used to sign the ServerKeyExchange or
400    /// > CertificateVerify message, for example "rsa_pss_rsae_sha256".
401    /// 
402    /// # Returns
403    /// 
404    /// An error will be returned if `sig_schm` contains non-ascii, non-printable and
405    /// whitespace.
406    /// 
407    /// Other error maybe returned too.
408    pub 
409    fn add_ssl_sig_scheme_borrow<'a>(&mut self, sig_schm: &'a str) -> HaProxRes<()>
410    {
411        common::is_printable_ascii_nowp(sig_schm, "sig_schm")?;
412
413        return self.hdr.add_tlv(PP2Tlvs::TypeSubTypeSslSigScheme(Cow::Borrowed(sig_schm)), Some(self.constraints));
414    }
415}
416
417/// A root (main) TLV writer.
418#[derive(Debug)]
419pub struct TlType<'s>
420{
421    /// A reference to the header.
422    hdr: &'s mut ProxyHdrV2<HdrV2OpProxy>,
423
424    /// A TLVs which can be set on root level.
425    constraints: &'static [std::ops::RangeInclusive<u8>]
426}
427
428impl<'s> TlType<'s>
429{
430    fn new(hdr: &'s mut ProxyHdrV2<HdrV2OpProxy>, constraints: &'static [std::ops::RangeInclusive<u8>]) -> Self
431    {
432        return
433            Self
434            {
435                hdr,
436                constraints
437            };
438    }
439
440    /// Adds the Application-Layer Protocol Negotiation (ALPN)
441    /// 
442    /// The input is not validated! ToDo?
443    pub 
444    fn add_alpn<'a>(&mut self, alpns: impl Iterator<Item = &'a [u8]>) -> HaProxRes<()>
445    {
446        return 
447            self
448                .add_tlv(
449                    PP2Tlvs::TypeAlpn( 
450                        alpns
451                            .map(|v| Cow::Borrowed(v))
452                            .collect::<Vec<Cow<'a, [u8]>>>()
453                            .into() 
454                    ), 
455                    None
456                );
457    }
458
459    /// Adds padding 3 bytes.
460    pub 
461    fn add_noop(&mut self) -> HaProxRes<()>
462    {
463        return self.add_tlv(PP2Tlvs::TypeNoop, None);
464    }
465
466    /// Adds  US-ASCII string representation of the namespace's name
467    /// 
468    /// # Returns
469    /// 
470    /// An error will be returned if `ns` contains non-ascii, non-printable and
471    /// whitespace.
472    /// 
473    /// Other error maybe returned too. 
474    pub 
475    fn add_netns(&mut self, ns: impl Into<String>) -> HaProxRes<()>
476    {
477        let ns_str = ns.into();
478
479        common::is_printable_ascii_nowp(&ns_str, "net_ns")?;
480
481        return self.add_tlv(PP2Tlvs::TypeNetNs(ns_str.into()), None);
482    }
483
484    /// Adds (borrowed) US-ASCII string representation of the namespace's name
485    /// 
486    /// # Returns
487    /// 
488    /// An error will be returned if `ns` contains non-ascii, non-printable and
489    /// whitespace.
490    /// 
491    /// Other error maybe returned too. 
492    pub 
493    fn add_netns_borrowed<'a>(&mut self, ns: &'a str) -> HaProxRes<()>
494    {
495        common::is_printable_ascii_nowp(ns, "net_ns")?;
496
497        return self.add_tlv(PP2Tlvs::TypeNetNs(Cow::Borrowed(ns)), None);
498    }
499
500    /// Adds a 32-bit number storing the CRC32c checksum of the PROXY protocol header
501    /// 
502    /// It will be calculated later after finalization.
503    pub 
504    fn add_crc32(&mut self) -> HaProxRes<()>
505    {
506        return self.add_tlv(PP2Tlvs::TypeCrc32c(0), None);
507    }
508
509    /// Adds a uniqID. See [PP2TlvUniqId]. 128 bytes max.
510    /// 
511    /// The input is not validated! ToDo?
512    pub 
513    fn add_uniq_id<ID: PP2TlvUniqId>(&mut self, au: ID) -> HaProxRes<()>
514    {
515        let uniq_id = au.into_bytes();
516
517        return self.add_tlv(PP2Tlvs::TypeUniqId(uniq_id.into()), None);
518    }
519
520    /// Adds a uniqID as slice borrow. See [PP2TlvUniqId]. 128 bytes max.
521    /// 
522    /// The input is not validated! ToDo?
523    pub 
524    fn add_uniq_id_borrow<ID: PP2TlvUniqId>(&mut self, au: ID) -> HaProxRes<()>
525    {
526        let uniq_id = au.as_slice();
527
528        return self.add_tlv(PP2Tlvs::TypeUniqId(Cow::Borrowed(uniq_id)), None);
529    }
530
531    /// Adds "SNI" i.e the "server_name" extension as defined by RFC3546.
532    /// 
533    /// The input is not validated! ToDo?
534    pub 
535    fn add_authority(&mut self, authority: impl Into<String>) -> HaProxRes<()>
536    {
537        return self.add_tlv(PP2Tlvs::TypeAuthority(authority.into().into()), None);
538    }
539
540    /// Adds "SNI" borrow i.e the "server_name" extension as defined by RFC3546.
541    pub 
542    fn add_authority_borrrow<'a>(&mut self, authority: &'a str) -> HaProxRes<()>
543    {
544        return self.add_tlv(PP2Tlvs::TypeAuthority(Cow::Borrowed(authority)), None);
545    }
546
547    // --- SSL and subtypes ---
548
549    /// Adds SSL properties.
550    pub 
551    fn add_ssl(self, client: PP2TlvClient, verify: u32) -> HaProxRes<TlvSubTypeSsl<'s>>
552    {
553        return TlvSubTypeSsl::new(self, PP2Tlvs::TLV_TYPE_SSL_SUB_RANGE, client, verify);
554    }
555
556    /// Adding external TLV. A [PP2TlvDump] should be implemeted by the `tlv`.
557    pub 
558    fn add_tlv<TLV: PP2TlvDump>(&mut self, tlv: TLV, 
559        opt_constr: Option<&'static [std::ops::RangeInclusive<u8>]>) -> HaProxRes<()>
560    {
561        let tlv_id: u8 = tlv.get_type();
562
563        let constr = opt_constr.unwrap_or(self.constraints);
564
565        if constr.iter().any(|idr| idr.contains(&tlv_id)) == false
566        {
567            return_error!(ArgumentEinval, "TLV: {} is subtype of other type or type!", tlv);
568        }
569
570        if tlv_id == PP2Tlvs::TypeCrc32c(0).into()
571        {
572            if self.hdr.crc_tlv_offset != 0
573            {
574                return_error!(ArgumentEinval, "duplicate PLT CRC!");
575            }
576
577            self.hdr.crc_tlv_offset = self.hdr.buffer.position();
578        }
579
580        // write type
581        self.hdr.buffer.write_u8(tlv_id).map_err(common::map_io_err)?;
582
583        // store size_position
584        let size_pos = self.hdr.buffer.position();
585
586        // write fake size
587        self.hdr.buffer.write_u16::<BigEndian>(0).map_err(common::map_io_err)?;   
588
589        // write TLV's content
590        tlv.dump(&mut self.hdr.buffer)?;
591
592        // write size
593        let cur_pos = self.hdr.buffer.position();
594        self.hdr.buffer.set_position(size_pos);
595
596        // size
597        self.hdr.buffer.write_u16::<BigEndian>((cur_pos - size_pos - 2) as u16).map_err(common::map_io_err)?;
598
599        // restore cursor
600        self.hdr.buffer.set_position(cur_pos);
601
602        return Ok(());
603    }
604}
605
606/// A Proxy Network packet composer.
607/// 
608/// Depending on the type of the generic `OPC` a different composing options
609/// are available.
610/// 
611/// OPC:
612/// 
613/// * [HdrV2OpLocal] - a local operation as described in the proxy documentation.
614/// 
615/// * [HdrV2OpProxy] - a proxy operation as described in the proxy documentation.
616/// 
617/// # Arguments
618/// 
619/// * `OPC` - [ProxyV2OpCode] a proxy opcode. It is intentionally made as a generic
620///     to separate the options which are availabe for each type of HaProxy message.
621/// 
622/// # Example
623/// 
624/// ```ignore
625/// // creating new instance
626/// let mut comp = 
627///     ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
628/// 
629/// // aquiring the plts to set fields
630/// let plts = comp.set_plts();
631/// 
632/// // adding ssl info
633/// let mut ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
634/// ssl.add_ssl_sub_version("TLSv1.2").unwrap();
635///     
636/// // done writing ssl, returning the plts
637/// let mut plts = ssl.done().unwrap();
638/// 
639/// // a custom TLV
640/// let cust_plt = ProxyV2Dummy2::SomeTlvName(0x01020304, 0x05060708);
641/// 
642/// plts.add_tlv(cust_plt, Some(&[0xE0..=0xE0])).unwrap();
643/// 
644/// drop(plts);
645/// 
646/// // convert to vec of bytes
647/// let pkt: Vec<u8> = comp.try_into().unwrap();
648/// ```
649/// 
650#[derive(Clone, Debug)]
651pub struct ProxyHdrV2<OPC: ProxyV2OpCode>
652{
653    /// Preallocard space for header.
654    buffer: Cursor<Vec<u8>>,
655
656    /// Offset to the location of the TLV CRC
657    crc_tlv_offset: u64,
658
659    /// Phantom for the [ProxyV2OpCode]
660    _p: PhantomData<OPC>,
661}
662
663impl<OPC: ProxyV2OpCode> ProxyHdrV2<OPC>
664{
665    /// Offset to the message length field.
666    pub const HDR_MSG_LEN_OFFSET: u64 = offset_of!(protocol_raw::ProxyHdrV2, len) as u64;
667}
668
669/// All functionality available for the opcode LOCAL.
670impl ProxyHdrV2<HdrV2OpLocal>
671{
672    /// Creates a new message of type LOCAL [HdrV2Command::LOCAL].
673    pub 
674    fn new() -> Vec<u8>
675    {
676        return protocol_raw::MSG_HEADER_LOCAL_V2.to_vec();
677    }
678}
679
680/// All functionality available for the opcode PROXY.
681impl ProxyHdrV2<HdrV2OpProxy>
682{
683    /// Creates new message of type PROXY [HdrV2Command::PROXY]. A program should provide the
684    /// type of the transport and address of the source and destination. The message compose instance 
685    /// is returned which already contains a prepared header.
686    /// 
687    /// # Arguments
688    /// 
689    /// * `transport` - [ProxyTransportFam] a type of the transport.
690    /// 
691    /// * `address` - a source and destination address encapsulated in [ProxyV2Addr].
692    /// 
693    /// # Returns
694    /// 
695    /// A [HaProxRes] is returned:
696    /// 
697    /// * [Result::Ok] - with the instance
698    /// 
699    /// * [Result::Err] - error description
700    pub 
701    fn new(transport: ProxyTransportFam, address: ProxyV2Addr) -> HaProxRes<Self>
702    {
703        let buf: Vec<u8> = 
704            Vec::with_capacity(size_of::<protocol_raw::ProxyHdrV2>());
705
706        let mut cur = Cursor::new(buf);
707        // writing header
708
709        // header_magic
710        cur.write_all(protocol_raw::HEADER_MAGIC_V2).map_err(common::map_io_err)?;
711
712        // protocol version and command 
713        cur.write_u8(0x20 | HdrV2OpProxy::OPCODE).map_err(common::map_io_err)?;
714
715        // addr type and transport
716        cur.write_u8(((address.as_addr_family() as u8) << 4) | transport as u8).map_err(common::map_io_err)?;
717
718        // length offset is known, so it will be updated later
719        cur.write_u16::<BigEndian>(address.get_len()).map_err(common::map_io_err)?;
720
721        // writing address
722        address.write(&mut cur)?;
723
724        return Ok(
725            Self
726            {
727                buffer: cur,
728                crc_tlv_offset: 0,
729                _p: PhantomData
730            }
731        );
732    }
733
734    /// Provides functionality to add the TLV to the payload of the message.
735    pub 
736    fn set_plts<'s>(&'s mut self) -> TlType<'s>
737    {
738        return TlType::new(self, PP2Tlvs::TLV_TYPE_MAIN_RANGES);
739    }
740
741    fn finalize(&mut self) -> HaProxRes<()>
742    {
743        let last_off = self.buffer.position();
744
745        // set position to HEADER LENGTH
746        self.buffer.set_position(Self::HDR_MSG_LEN_OFFSET);
747
748        let tlv_len = last_off - size_of::<protocol_raw::ProxyHdrV2>() as u64;
749
750        self.buffer.write_u16::<BigEndian>(tlv_len as u16).map_err(common::map_io_err)?;
751
752        if self.crc_tlv_offset > 0
753        {
754            // init hasher
755            let mut hasher = Hasher::new();
756
757            // calculate crc32
758            hasher.update(self.buffer.get_ref());
759
760            // set position to start of the CRC's plt payload
761            self.buffer.set_position(self.crc_tlv_offset + protocol::TLV_HEADER_LEN as u64);
762
763            // write back CRC
764            self.buffer.write_u32::<BigEndian>(hasher.finalize()).map_err(common::map_io_err)?;
765        }
766        return Ok(());
767    }
768
769    
770}
771
772/// Converts the instance to the vector. The message will be finalized i.e
773/// length recalculated, CRC updated.
774impl TryFrom<ProxyHdrV2<HdrV2OpProxy>> for Vec<u8>
775{
776    type Error = HaProxErr;
777
778    fn try_from(mut value: ProxyHdrV2<HdrV2OpProxy>) -> Result<Self, Self::Error> 
779    {
780        value.finalize()?;
781
782        return Ok(value.buffer.into_inner());
783    }
784}
785
786impl<'sz> PP2TlvDump for PP2Tlvs<'sz>
787{
788    fn get_type(&self) -> u8 
789    {
790        return self.into();
791    }
792
793    fn dump(&self, cur: &mut Cursor<Vec<u8>>) -> HaProxRes<()> 
794    {
795        match self
796        {
797            Self::TypeAlpn(items) => 
798            {
799                // payload
800                for alpn in items.iter()//.map(|v| v.as_bytes())
801                {
802                    // length u16
803                    cur.write_u16::<BigEndian>(alpn.len() as u16).map_err(common::map_io_err)?;
804
805                    // alpn bytes
806                    cur.write_all(alpn).map_err(common::map_io_err)?;
807                }
808            },
809            Self::TypeAuthority(auth) => 
810            {
811                // authority
812                cur.write_all(auth.as_bytes()).map_err(common::map_io_err)?;
813            },
814            Self::TypeCrc32c(crc) => 
815            {
816                cur.write_u32::<BigEndian>(*crc).map_err(common::map_io_err)?;
817            },
818            Self::TypeNoop => 
819            {
820
821            },
822            Self::TypeUniqId(items) =>
823            {
824                cur.write_all(items).map_err(common::map_io_err)?;
825            },
826            Self::TypeSsl{client, verify} => 
827            {
828                // client
829                cur.write_u8(client.bits()).map_err(common::map_io_err)?;
830
831                // verify
832                cur.write_u32::<BigEndian>(*verify).map_err(common::map_io_err)?;
833            },
834            Self::TypeSubtypeSslVersion(v) => 
835            {
836                cur.write_all(v.as_bytes()).map_err(common::map_io_err)?;
837            },
838            Self::TypeSubtypeSslCn(cn) => 
839            {
840                cur.write_all(cn.as_bytes()).map_err(common::map_io_err)?;
841            },
842            Self::TypeSubtypeSslCipher(c) => 
843            {
844                cur.write_all(c.as_bytes()).map_err(common::map_io_err)?;
845            },
846            Self::TypeSubtypeSslSigAlg(sa) => 
847            {
848                cur.write_all(sa.as_bytes()).map_err(common::map_io_err)?;
849            },
850            Self::TypeSubtypeSslKeyAlg(ka) => 
851            {
852                cur.write_all(ka.as_bytes()).map_err(common::map_io_err)?;
853            },
854            Self::TypeSubTypeSslGroup(group) => 
855            {
856                cur.write_all(group.as_bytes()).map_err(common::map_io_err)?;
857            },
858            Self::TypeSubTypeSslSigScheme(sig) => 
859            {
860                cur.write_all(sig.as_bytes()).map_err(common::map_io_err)?;
861            }
862            Self::TypeNetNs(ns) => 
863            {
864                cur.write_all(ns.as_bytes()).map_err(common::map_io_err)?;
865            },
866        }
867
868        return Ok(());
869    }
870}
871
872#[cfg(test)]
873mod tests
874{
875    use std::{fmt, io::Cursor};
876
877    use byteorder::{BigEndian, WriteBytesExt};
878
879    use crate::{common::map_io_err, protocol_v2::{protocol::{PP2TlvClient, PP2Tlvs}, PP2TlvDump}, HaProxRes, ProxyTransportFam, ProxyV2Addr};
880
881    use super::{HdrV2OpProxy, ProxyHdrV2};
882
883    #[test]
884    fn test_comp0()
885    {
886        let addr = ProxyV2Addr::try_from(("127.0.0.1:39754", "127.0.0.67:11883")).unwrap();
887        
888        let mut comp = 
889            ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
890
891        let plts = comp.set_plts();
892
893        let mut ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
894
895        ssl.add_ssl_sub_version("TLSv1.2").unwrap();
896        
897        ssl.done().unwrap();
898
899        let pkt: Vec<u8> = comp.try_into().unwrap();
900
901        let ctrl = 
902b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\x21\x11\x00\x1e\
903\x7f\x00\x00\x01\x7f\x00\x00\x43\x9b\x4a\x2e\x6b\x20\x00\x0f\x01\
904\x00\x00\x00\x00\x21\x00\x07\x54\x4c\x53\x76\x31\x2e\x32";
905
906        assert_eq!(pkt.as_slice(), ctrl.as_slice());
907    }
908
909    #[test]
910    fn test_comp1()
911    {
912        #[derive(Clone, Debug)]
913        pub enum ProxyV2Dummy2 
914        {
915            SomeTlvName(u32, u32),
916        }
917
918        impl fmt::Display for ProxyV2Dummy2
919        {
920            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
921            {
922                write!(f, "DUMMY external reader")
923            }
924        }
925
926        impl PP2TlvDump for ProxyV2Dummy2
927        {
928            fn get_type(&self) -> u8 
929            {
930                #[allow(irrefutable_let_patterns)]
931                let Self::SomeTlvName(..) = self else { panic!("wrong") };
932
933                return 0xE0;
934            }
935
936            fn dump(&self, cur: &mut Cursor<Vec<u8>>) -> HaProxRes<()> 
937            {
938                match self
939                {
940                    Self::SomeTlvName(arg0, arg1) =>
941                    {
942                        cur.write_u32::<BigEndian>(*arg0).map_err(map_io_err)?;
943                        cur.write_u32::<BigEndian>(*arg1).map_err(map_io_err)?;
944                    }
945                }
946
947                return Ok(());
948            }
949        }
950
951
952
953        let addr = ProxyV2Addr::try_from(("127.0.0.1:39754", "127.0.0.67:11883")).unwrap();
954        
955        let mut comp = 
956            ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
957
958        let plts = comp.set_plts();
959
960        let mut ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
961
962        ssl.add_ssl_sub_version("TLSv1.2").unwrap();
963        
964        let mut plts = ssl.done().unwrap();
965
966
967        let cust_plt = ProxyV2Dummy2::SomeTlvName(0x01020304, 0x05060708);
968
969        plts.add_tlv(cust_plt, Some(&[0xE0..=0xE0])).unwrap();
970
971        drop(plts);
972
973        let pkt: Vec<u8> = comp.try_into().unwrap();
974
975        let ctrl = 
976b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\x21\x11\x00\x29\
977\x7f\x00\x00\x01\x7f\x00\x00\x43\x9b\x4a\x2e\x6b\x20\x00\x0f\x01\
978\x00\x00\x00\x00\x21\x00\x07\x54\x4c\x53\x76\x31\x2e\x32\xE0\x00\
979\x08\x01\x02\x03\x04\x05\x06\x07\x08";
980
981        assert_eq!(pkt.as_slice(), ctrl.as_slice());
982    }
983
984    #[test]
985    fn test_alpns()
986    {
987        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
988
989        let alpn = PP2Tlvs::TypeAlpn(vec![b"test".as_slice().to_vec().into()].into());
990
991        alpn.dump(&mut cur).unwrap();
992
993       // let op: u8 = (&alpn).into();
994
995        let reference = b"\x00\x04test";
996
997        let generated = cur.into_inner();
998
999        assert_eq!(generated.as_slice(), reference.as_slice());
1000    }
1001
1002    #[test]
1003    fn test_authority()
1004    {
1005        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1006
1007        let alpn = PP2Tlvs::TypeAuthority("tset".into());
1008
1009        alpn.dump(&mut cur).unwrap();
1010
1011        //let op: u8 = (&alpn).into();
1012
1013        let reference = b"tset";
1014
1015        let generated = cur.into_inner();
1016
1017        assert_eq!(generated.as_slice(), reference.as_slice());
1018    }
1019
1020    #[test]
1021    fn test_crc32()
1022    {
1023        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1024
1025        let alpn = PP2Tlvs::TypeCrc32c(0xABCDEF01);
1026
1027        alpn.dump(&mut cur).unwrap();
1028
1029        //let op: u8 = (&alpn).into();
1030
1031        let reference = b"\xab\xcd\xef\x01";
1032
1033        let generated = cur.into_inner();
1034
1035        assert_eq!(generated.as_slice(), reference.as_slice());
1036    }
1037
1038    #[test]
1039    fn test_netns()
1040    {
1041        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1042
1043        let alpn = PP2Tlvs::TypeNetNs("tstt".into());
1044
1045        alpn.dump(&mut cur).unwrap();
1046
1047        //let op: u8 = (&alpn).into();
1048
1049        let reference = b"tstt";
1050
1051        let generated = cur.into_inner();
1052
1053        assert_eq!(generated.as_slice(), reference.as_slice());
1054    }
1055
1056    #[test]
1057    fn test_noop()
1058    {
1059        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1060
1061        let alpn = PP2Tlvs::TypeNoop;
1062
1063        alpn.dump(&mut cur).unwrap();
1064
1065        let reference = b"";
1066
1067        let generated = cur.into_inner();
1068
1069        assert_eq!(generated.as_slice(), reference.as_slice());
1070    }
1071
1072    #[test]
1073    fn test_ssl()
1074    {
1075        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1076
1077        let alpn = PP2Tlvs::TypeSsl{ client: PP2TlvClient::PP2_CLIENT_SSL, verify: 0x00003210};
1078
1079        alpn.dump(&mut cur).unwrap();
1080
1081        //let op: u8 = (&alpn).into();
1082
1083        let reference = b"\x01\x00\x00\x32\x10";
1084
1085        let generated = cur.into_inner();
1086
1087        assert_eq!(generated.as_slice(), reference.as_slice());
1088    }
1089
1090    #[test]
1091    fn test_uniqid()
1092    {
1093        const ID: &'static [u8] = b"ABCD12345678901234567890";
1094
1095        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1096
1097        let alpn = PP2Tlvs::TypeUniqId(ID.into());
1098
1099        alpn.dump(&mut cur).unwrap();
1100
1101        let reference = ID;
1102
1103        let generated = cur.into_inner();
1104
1105        assert_eq!(generated.as_slice(), reference);
1106    }
1107
1108    #[test]
1109    fn test_ssl_cipher()
1110    {
1111        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1112
1113        let ciph = PP2Tlvs::TypeSubtypeSslCipher("ECDHE-RSA-AES128-GCM-SHA256".into());
1114
1115        ciph.dump(&mut cur).unwrap();
1116
1117       // let op: u8 = (&ciph).into();
1118
1119        let reference = b"ECDHE-RSA-AES128-GCM-SHA256";
1120
1121        let generated = cur.into_inner();
1122
1123        assert_eq!(generated.as_slice(), reference.as_slice());
1124    }
1125
1126    #[test]
1127    fn test_ssl_cn()
1128    {
1129        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1130
1131        let cn = PP2Tlvs::TypeSubtypeSslCn("example.com".into());
1132
1133        cn.dump(&mut cur).unwrap();
1134
1135        //let op: u8 = (&cn).into();
1136
1137        let reference = b"example.com";
1138
1139        let generated = cur.into_inner();
1140
1141        assert_eq!(generated.as_slice(), reference.as_slice());
1142    }
1143
1144    #[test]
1145    fn test_ssl_keyalg()
1146    {
1147        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1148
1149        let keyalg = PP2Tlvs::TypeSubtypeSslKeyAlg("RSA2048".into());
1150
1151        keyalg.dump(&mut cur).unwrap();
1152
1153       // let op: u8 = (&keyalg).into();
1154
1155        let reference = b"RSA2048";
1156
1157        let generated = cur.into_inner();
1158
1159        assert_eq!(generated.as_slice(), reference.as_slice());
1160    }
1161
1162    #[test]
1163    fn test_ssl_sigalg()
1164    {
1165        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1166
1167        let sigalg = PP2Tlvs::TypeSubtypeSslSigAlg("SHA256".into());
1168
1169        sigalg.dump(&mut cur).unwrap();
1170
1171       // let op: u8 = (&sigalg).into();
1172
1173        let reference = b"SHA256";
1174
1175        let generated = cur.into_inner();
1176
1177        assert_eq!(generated.as_slice(), reference.as_slice());
1178    }
1179
1180    #[test]
1181    fn test_ssl_version()
1182    {
1183        let mut cur = Cursor::new(Vec::<u8>::with_capacity(64));
1184
1185        let vers = PP2Tlvs::TypeSubtypeSslVersion("TLSv1_3".into());
1186
1187        vers.dump(&mut cur).unwrap();
1188
1189        //let op: u8 = (&vers).into();
1190
1191        let reference = b"TLSv1_3";
1192
1193        let generated = cur.into_inner();
1194
1195        assert_eq!(generated.as_slice(), reference.as_slice());
1196    }
1197}