haprox_rs/protocol_v1/
mod.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
15pub mod protocol_parser;
16pub mod protocol_composer;
17
18use core::fmt;
19use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
20
21use crate::{HaProxRes, map_error, protocol_raw, return_error};
22
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ProtocolV1Inet
26{
27    Tcp4, Tcp6, None
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum HapProtoV1
32{
33    TCP4
34    {
35        src_addr: Ipv4Addr,
36        dst_addr: Ipv4Addr,
37        src_port: u16,
38        dst_port: u16,
39    },
40    TCP6
41    {
42        src_addr: Ipv6Addr,
43        dst_addr: Ipv6Addr,
44        src_port: u16,
45        dst_port: u16,
46    },
47    UNKNOWN,
48}
49
50impl fmt::Display for HapProtoV1
51{
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
53    {
54        let s: String = self.into();
55
56        write!(f, "{}", s)
57    }
58}
59
60impl From<&HapProtoV1> for String
61{
62    fn from(value: &HapProtoV1) -> Self 
63    {
64        match value
65        {
66            HapProtoV1::TCP4{ src_addr, dst_addr, src_port, dst_port } =>
67            {
68                let out = 
69                    [
70                        protocol_raw::HEADER_MAGIC_V1_STR, protocol_raw::HEADER_V1_WSPACE,
71                        protocol_raw::HEADER_V1_INET_TCP4, protocol_raw::HEADER_V1_WSPACE,
72                        src_addr.to_string().as_str(), protocol_raw::HEADER_V1_WSPACE,
73                        dst_addr.to_string().as_str(), protocol_raw::HEADER_V1_WSPACE,
74                        src_port.to_string().as_str(), protocol_raw::HEADER_V1_WSPACE,
75                        dst_port.to_string().as_str(), protocol_raw::HEADER_V1_EOM
76                    ]
77                    .concat();
78
79                return out;
80            },
81            HapProtoV1::TCP6{ src_addr, dst_addr, src_port, dst_port } =>
82            {
83                let out = 
84                    [
85                        protocol_raw::HEADER_MAGIC_V1_STR, protocol_raw::HEADER_V1_WSPACE,
86                        protocol_raw::HEADER_V1_INET_TCP6, protocol_raw::HEADER_V1_WSPACE,
87                        src_addr.segments().iter().map(|v| format!("{:04x}", v)).collect::<Vec<String>>().join(":").as_str(), protocol_raw::HEADER_V1_WSPACE,
88                        dst_addr.segments().iter().map(|v| format!("{:04x}", v)).collect::<Vec<String>>().join(":").as_str(), protocol_raw::HEADER_V1_WSPACE,
89                        src_port.to_string().as_str(), protocol_raw::HEADER_V1_WSPACE,
90                        dst_port.to_string().as_str(), protocol_raw::HEADER_V1_EOM
91                    ]
92                    .concat();
93
94                return out;
95            },
96            HapProtoV1::UNKNOWN =>
97            {
98                let out = 
99                    [
100                        protocol_raw::HEADER_MAGIC_V1_STR, protocol_raw::HEADER_V1_WSPACE,
101                        protocol_raw::HEADER_V1_INET_UNKNWON, protocol_raw::HEADER_V1_EOM
102                    ]
103                    .concat();
104
105                return out;
106            }
107        }
108        
109    }
110}
111
112
113impl From<HapProtoV1> for String
114{
115    fn from(value: HapProtoV1) -> Self 
116    {
117        return (&value).into();
118    }
119}
120
121impl HapProtoV1
122{
123    pub(super) 
124    fn unknown() -> Self
125    {
126        return Self::UNKNOWN;
127    }
128
129    pub(super) 
130    fn from_ip_port(
131        src_ip: IpAddr, 
132        dst_ip: IpAddr, 
133        src_port: u16, 
134        dst_port: u16
135    ) -> HaProxRes<Self>
136    {
137        
138        if src_ip.is_ipv4() == true
139        {
140            let IpAddr::V4(dst_addr) = dst_ip
141                else
142                {
143                    return_error!(ArgumentEinval, "dst_addr is not IPv4");
144                };
145
146            let IpAddr::V4(src_addr) = src_ip
147                else
148                {
149                    return_error!(ArgumentEinval, "src_addr is not IPv4");
150                };
151
152            return Ok(
153                Self::TCP4{ src_addr, dst_addr, src_port, dst_port }
154            )
155        }
156        else
157        {
158            let IpAddr::V6(src_addr) = src_ip
159                else
160                {
161                    return_error!(ArgumentEinval, "src_addr is not IPv6");
162                };
163
164            let IpAddr::V6(dst_addr) = dst_ip
165                else
166                {
167                    return_error!(ArgumentEinval, "dst_addr is not IPv6");
168                };
169
170            return Ok(
171                Self::TCP6{ src_addr, dst_addr, src_port, dst_port }
172            )
173        }
174    }
175
176    pub 
177    fn from_raw(
178        inet: &str, 
179        src_ip: Option<&str>, 
180        dst_ip: Option<&str>, 
181        src_port: Option<&str>, 
182        dst_port: Option<&str>
183    ) -> HaProxRes<Self>
184    {
185        if inet == protocol_raw::HEADER_V1_INET_UNKNWON
186        {
187            return Ok(Self::UNKNOWN);
188        }
189
190        if src_ip.is_none() == true || dst_ip.is_none() == true ||
191            src_port.is_none() == true || dst_port.is_none() == true
192        {
193            return_error!(MalformedData, "missing field/s '{:?}' '{:?}' '{:?}' '{:?}'", 
194                src_ip, dst_ip, src_port, dst_port);
195        }
196
197        if inet == protocol_raw::HEADER_V1_INET_TCP4
198        {
199            return Ok(
200                Self::TCP4 
201                { 
202                    src_addr: 
203                        src_ip.as_ref().unwrap().parse().map_err(|e| 
204                                map_error!(MalformedData, "cannot parse src_addr '{}' err: '{}", src_ip.unwrap(), e)
205                            )?,
206                    dst_addr: 
207                        dst_ip.as_ref().unwrap().parse().map_err(|e| 
208                                map_error!(MalformedData, "cannot parse dst_addr '{}' err: '{}", dst_ip.unwrap(), e)
209                            )?, 
210                    src_port: 
211                        src_port.as_ref().unwrap().parse().map_err(|e| 
212                                map_error!(MalformedData, "cannot parse src_port '{}' err: '{}", src_port.unwrap(), e)
213                            )?, 
214                    dst_port: 
215                        dst_port.as_ref().unwrap().parse().map_err(|e| 
216                                map_error!(MalformedData, "cannot parse dst_port '{}' err: '{}", dst_port.unwrap(), e)
217                            )?
218                }
219            );
220        }
221        else if inet == protocol_raw::HEADER_V1_INET_TCP6
222        {
223            return Ok(
224                Self::TCP6 
225                { 
226                    src_addr: 
227                        src_ip.as_ref().unwrap().parse().map_err(|e| 
228                                map_error!(MalformedData, "cannot parse src_addr '{}' err: '{}", src_ip.unwrap(), e)
229                            )?,
230                    dst_addr: 
231                        dst_ip.as_ref().unwrap().parse().map_err(|e| 
232                                map_error!(MalformedData, "cannot parse dst_addr '{}' err: '{}", dst_ip.unwrap(), e)
233                            )?, 
234                    src_port: 
235                        src_port.as_ref().unwrap().parse().map_err(|e| 
236                                map_error!(MalformedData, "cannot parse src_port '{}' err: '{}", src_port.unwrap(), e)
237                            )?, 
238                    dst_port: 
239                        dst_port.as_ref().unwrap().parse().map_err(|e| 
240                                map_error!(MalformedData, "cannot parse dst_port '{}' err: '{}", dst_port.unwrap(), e)
241                            )?
242                }
243            );
244        }
245        else
246        {
247            return_error!(MalformedData, "unknown INET: '{}'", inet);
248        }
249    }
250
251    pub 
252    fn get_src_addr(&self) -> Option<IpAddr>
253    {
254        match self
255        {
256            Self::TCP4 { src_addr, .. } => 
257                return Some(IpAddr::V4(*src_addr)),
258            Self::TCP6 { src_addr, .. } => 
259                return Some(IpAddr::V6(*src_addr)),
260            Self::UNKNOWN => 
261                return None,
262        }
263    }
264
265    pub 
266    fn get_dst_addr(&self) -> Option<IpAddr>
267    {
268        match self
269        {
270            Self::TCP4 { dst_addr, .. } => 
271                return Some(IpAddr::V4(*dst_addr)),
272            Self::TCP6 { dst_addr, .. } => 
273                return Some(IpAddr::V6(*dst_addr)),
274            Self::UNKNOWN => 
275                return None,
276        }
277    }
278
279    pub 
280    fn get_src_port(&self) -> Option<u16>
281    {
282        match self
283        {
284            Self::TCP4 { src_port, .. } => 
285                return Some(*src_port),
286            Self::TCP6 { src_port, .. } => 
287                return Some(*src_port),
288            Self::UNKNOWN => 
289                return None,
290        }
291    }
292
293    pub 
294    fn get_dst_port(&self) -> Option<u16>
295    {
296        match self
297        {
298            Self::TCP4 { dst_port, .. } => 
299                return Some(*dst_port),
300            Self::TCP6 { dst_port, .. } => 
301                return Some(*dst_port),
302            Self::UNKNOWN => 
303                return None,
304        }
305    }
306
307    pub 
308    fn get_inet(&self) -> ProtocolV1Inet
309    {
310        match self
311        {
312            Self::TCP4{ .. } => 
313                return ProtocolV1Inet::Tcp4,
314            Self::TCP6 { .. } => 
315                return ProtocolV1Inet::Tcp6,
316            Self::UNKNOWN => 
317                return ProtocolV1Inet::None
318        }
319    }
320}
321
322#[cfg(test)]
323mod tests_proto
324{
325    use crate::HapProtoV1;
326
327    #[test]
328    fn simple_test1()
329    {
330        let s: String = 
331            HapProtoV1::TCP4 
332            { 
333                src_addr: "255.255.255.255".parse().unwrap(), 
334                dst_addr: "255.255.255.255".parse().unwrap(), 
335                src_port: 65535, 
336                dst_port: 65535 
337            }
338            .into();
339
340        assert_eq!(s.as_str(), "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n");
341    }
342
343    #[test]
344    fn simple_test2()
345    {
346        let s: String = 
347            HapProtoV1::TCP4 
348            { 
349                src_addr: "192.168.1.1".parse().unwrap(), 
350                dst_addr: "10.8.0.1".parse().unwrap(), 
351                src_port: 23456, 
352                dst_port: 12345 
353            }
354            .into();
355
356        assert_eq!(s.as_str(), "PROXY TCP4 192.168.1.1 10.8.0.1 23456 12345\r\n");
357    }
358
359    #[test]
360    fn simple_test3()
361    {
362        let s: String = 
363            HapProtoV1::TCP6
364            { 
365                src_addr: "0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f".parse().unwrap(), 
366                dst_addr: "4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393".parse().unwrap(), 
367                src_port: 23456, 
368                dst_port: 12345 
369            }
370            .into();
371
372        assert_eq!(s.as_str(), "PROXY TCP6 0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f 4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393 23456 12345\r\n");
373    }
374
375    #[test]
376    fn simple_test4()
377    {
378        let s: String = 
379            HapProtoV1::TCP6
380            { 
381                src_addr: "0acf:5d35:b4c4:0000:2442:2f17:c6f9:5b7f".parse().unwrap(), 
382                dst_addr: "4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393".parse().unwrap(), 
383                src_port: 23456, 
384                dst_port: 12345 
385            }
386            .into();
387
388        assert_eq!(s.as_str(), "PROXY TCP6 0acf:5d35:b4c4:0000:2442:2f17:c6f9:5b7f 4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393 23456 12345\r\n");
389    }
390
391    #[test]
392    fn simple_test5()
393    {
394        let s: String = 
395            HapProtoV1::UNKNOWN.into();
396
397        assert_eq!(s.as_str(), "PROXY UNKNOWN\r\n");
398    }
399}