haprox_rs/
common.rs

1use std::io::{self, Cursor, Error, ErrorKind};
2
3use crate::{error::{HaProxErr, HaProxRes}, map_error, protocol_raw, return_error};
4
5#[allow(unused)]
6pub(crate)
7fn is_printable_utf8(u: &str, err_lbl: &str) -> HaProxRes<()>
8{
9    for p in u.chars()
10    {
11        // any character that can be printed except control chars
12        if p.is_control() == true || p.is_ascii_whitespace() == true || p.is_ascii_control() == true 
13        {
14            return_error!(ArgumentEinval,
15                "non-printable characters found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
16        }
17    }
18
19    return Ok(());
20}
21
22/// Chekcs if the argument `u` contains only printable, no whitespace characters to comply with US-ASCII.
23#[inline]
24pub(crate)
25fn is_printable_ascii_nowp(u: &str, err_lbl: &str) -> HaProxRes<()>
26{
27    return is_printable_ascii(u, err_lbl, false);
28}
29
30/// Chekcs if the argument `u` contains only printable, characters to comply with US-ASCII.
31pub(crate)
32fn is_printable_ascii(u: &str, err_lbl: &str, whitesp: bool) -> HaProxRes<()>
33{
34    for p in u.chars()
35    {
36        // any character that can be printed except control chars
37        if p.is_control() == true || 
38            (p.is_ascii_whitespace() == true && whitesp == false) || 
39            p.is_ascii_control() == true ||
40            p.is_ascii() == false
41        {
42            return_error!(ArgumentEinval,
43                "non-printable characters found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
44        }
45    }
46
47    return Ok(());
48}
49
50/// Chekcs if the argument `u` contains only printable, characters to comply with US-ASCII.
51pub(crate)
52fn check_printable_ascii_single_wp<'a>(u: &'a str, err_lbl: &str) -> HaProxRes<Option<&'a str>>
53{
54    let mut wpi = 0;
55
56    if u.ends_with(protocol_raw::HEADER_V1_EOM) == false
57    {
58        return Ok(None);
59    }
60
61    let uw_end = &u[0..u.len()-protocol_raw::HEADER_V1_EOM.len()];
62
63    for p in uw_end.chars()
64    {
65        // any character that can be printed except control chars
66        if p.is_control() == true || 
67            p.is_ascii() == false
68        {
69            return_error!(MalformedData,
70                "non-printable characters or non-ascii found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
71        }
72        else if p.is_ascii_whitespace() == true 
73        {
74            if wpi > 0
75            {
76                return_error!(MalformedData,
77                    "multiple spaces was found in '{}', for '{}'", sanitize_str_unicode(u), err_lbl);
78            }
79
80            wpi += 1;
81        }
82        else if p.is_ascii_control() == true 
83        {
84            return_error!(MalformedData,
85                "control chars in sequence in '{}', for '{}'", sanitize_str_unicode(u), err_lbl);            
86        }
87        else
88        {
89            wpi = 0;
90        }
91    }
92
93    return Ok(Some(uw_end));
94}
95
96/// Clears all control characlters leaving onlu pritable chars.
97pub(crate)
98fn sanitize_str_unicode(st: &str) -> String
99{
100    let mut out = String::with_capacity(st.len());
101
102    for c in st.chars()
103    {
104        if c.is_alphanumeric() == true ||
105            c.is_ascii_punctuation() == true ||
106            c == ' '
107        {
108            out.push(c);
109        }
110        else
111        {
112            let mut buf = [0_u8; 4];
113            c.encode_utf8(&mut buf);
114
115            let formatted: String = 
116                buf[0..c.len_utf8()].into_iter()
117                    .map(|c| format!("\\x{:02x}", c))
118                    .collect();
119
120            out.push_str(&formatted);
121        }
122    }
123
124    return out;
125}
126
127pub
128fn map_io_err(err: Error) -> HaProxErr
129{
130    return map_error!(IoError, "{}", err)
131}
132
133/// An external realization of borrowing using [Cursor].
134pub trait ReadExtZeroCopy<'zc>
135{
136    fn borrow_exact(&mut self, len: usize) -> std::io::Result<&'zc [u8]>;
137}
138
139impl<'zc> ReadExtZeroCopy<'zc> for Cursor<&'zc [u8]>
140{
141    fn borrow_exact(&mut self, len: usize) -> std::io::Result<&'zc [u8]> 
142    {
143        let start = self.position() as usize;
144
145        if self.get_ref().len() < start+len
146        {
147            return Err(
148                io::Error::new(ErrorKind::UnexpectedEof, 
149                    format!("len: {}, req: {}", self.get_ref().len(), start+len))
150            );
151        }
152        let out = &self.get_ref()[start..(start+len)];
153
154        self.set_position((start+len) as u64);
155
156        return Ok(out);
157    }
158}
159
160#[test]
161fn test_mult_wp()
162{
163    assert_eq!(
164        check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535  65535\r\n", "test").is_err(),
165        true
166    ); 
167
168    assert_eq!(
169        check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n", "test"),
170        Ok(Some("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535"))
171    ); 
172
173    assert_eq!(
174        check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535", "test"),
175        Ok(None)
176    );
177
178    assert_eq!(
179        check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r", "test"),
180        Ok(None)
181    ); 
182
183  
184    assert_eq!(
185        check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n\r\n", "test").is_err(),
186        true
187    ); 
188}