smtp_message/
lib.rs

1#![type_length_limit = "109238057"]
2
3pub use nom;
4
5mod command;
6mod data;
7mod misc;
8mod reply;
9
10// use command::*;
11// use data::*;
12use misc::*;
13// use reply::*;
14
15pub use command::{Command, ParameterName, Parameters};
16pub use data::{DataUnescapeRes, DataUnescaper, EscapedDataReader, EscapingDataWriter};
17pub use misc::{next_crlf, Email, Hostname, Localpart, MaybeUtf8, NextCrLfState, Path};
18pub use reply::{
19    EnhancedReplyCode, EnhancedReplyCodeClass, EnhancedReplyCodeSubject, Reply, ReplyCode,
20    ReplyCodeCategory, ReplyCodeKind, ReplyLine,
21};
22
23#[cfg(test)]
24use std::str;
25
26/// Used as `println!("{:?}", show_bytes(b))`
27#[cfg(test)]
28pub(crate) fn show_bytes(b: &[u8]) -> String {
29    if b.len() > 128 {
30        "{too long}".into()
31    } else if let Ok(s) = str::from_utf8(b) {
32        s.into()
33    } else {
34        format!("{:?}", b)
35    }
36}
37
38#[cfg(any(test, feature = "fuzz-targets"))]
39pub mod fuzz {
40    use super::*;
41
42    use std::{cmp, io::IoSlice};
43
44    use futures::{
45        executor,
46        io::{AsyncReadExt, AsyncWriteExt, Cursor},
47    };
48    use regex_automata::RegexBuilder;
49
50    pub fn escaping_then_unescaping(
51        data: Vec<Vec<Vec<u8>>>,
52        maxread: usize,
53        initread: usize,
54        mut readlen: Vec<usize>,
55    ) {
56        if readlen.is_empty() {
57            readlen.push(1);
58        }
59        // println!("==> NEW TEST");
60        // println!("  maxread = {}, initread = {}", maxread, initread);
61        // if readlen.len() < 128 {
62        // println!("  readlen = {:?}", readlen);
63        // } else {
64        // println!("  readlen is too long to be displayed");
65        // }
66        // if data
67        // .iter()
68        // .flat_map(|v| v.iter().map(|w| w.len()))
69        // .sum::<usize>()
70        // < 128
71        // {
72        // println!("  data = {:?}", data);
73        // } else {
74        // println!("  data is too long to be displayed");
75        // }
76
77        let mut wire = Vec::new();
78
79        // println!("Writing to the wire");
80        {
81            let mut writer = EscapingDataWriter::new(Cursor::new(&mut wire));
82            for write in data.iter() {
83                let mut written = 0;
84                let total_to_write = write.iter().map(|b| b.len()).sum::<usize>();
85                while written != total_to_write {
86                    let mut i = Vec::new();
87                    let mut skipped = 0;
88                    for s in write {
89                        if skipped + s.len() <= written {
90                            skipped += s.len();
91                            continue;
92                        }
93                        if written - skipped != 0 {
94                            i.push(IoSlice::new(&s[(written - skipped)..]));
95                            skipped = written;
96                        } else {
97                            i.push(IoSlice::new(s));
98                        }
99                    }
100                    written += executor::block_on(writer.write_vectored(&i)).unwrap();
101                }
102            }
103            executor::block_on(writer.finish()).unwrap();
104        }
105
106        // println!("Checking that the wire looks good");
107        {
108            // println!("  Wire is: {:?}", show_bytes(&wire));
109
110            assert!(wire == b".\r\n" || wire.ends_with(b"\r\n.\r\n"));
111
112            // Either there's no such sequence, or it's at the end
113            let reg = RegexBuilder::new()
114                .allow_invalid_utf8(true)
115                .build(r#"\r\n\.[^.]"#)
116                .unwrap();
117            assert!(
118                reg.find(&wire)
119                    .map(|(start, _)| start == wire.len() - 5)
120                    .unwrap_or(true)
121            );
122        }
123
124        // println!("Reading from the wire");
125        let mut read = Vec::new();
126        {
127            // Let's cap at 16MiB of buffer, or it's going to be too much. And minimum at 5,
128            // as documented in unescape, we need 4 bytes for unhandled data plus 1 byte for
129            // the newly read data.
130            let maxread = cmp::max(cmp::min(maxread, 16 * 1024 * 1024), 5);
131            let mut initbuf = vec![0; maxread];
132            let mut buf = vec![0; maxread];
133            let initread = cmp::min(cmp::min(initread, maxread), wire.len());
134            initbuf[..initread].copy_from_slice(&wire[..initread]);
135            wire = wire[initread..].to_owned();
136            let mut reader = EscapedDataReader::new(&mut initbuf, 0..initread, &wire[..]);
137            let mut unescaper = DataUnescaper::new(true);
138            let mut i = 0;
139            let mut start = 0;
140            loop {
141                // println!("  Entering the loop with i={}", i);
142                let read_size = cmp::min(cmp::max(1, readlen[i % readlen.len()]), maxread - start);
143                assert!(read_size > 0, "read_size = 0, bug in the test harness");
144                let bytes_read =
145                    executor::block_on(reader.read(&mut buf[start..start + read_size])).unwrap();
146                // println!(
147                // "    Raw read: {:?} (read_size {})",
148                // show_bytes(&buf[start..start + bytes_read]),
149                // read_size,
150                // );
151                if bytes_read == 0 {
152                    break;
153                }
154                let unesc = unescaper.unescape(&mut buf[..start + bytes_read]);
155                read.extend_from_slice(&buf[..unesc.written]);
156                // println!(
157                // "    Unescaped read: {:?}",
158                // show_bytes(&buf[..unesc.written])
159                // );
160                buf.copy_within(unesc.unhandled_idx..start + bytes_read, 0);
161                start = start + bytes_read - unesc.unhandled_idx;
162                i += 1;
163            }
164            // println!("  Exiting the loop");
165            reader.complete();
166            assert!(reader.get_unhandled().unwrap().is_empty());
167        }
168
169        // println!("Checking that the output matches");
170        {
171            let mut expected = data
172                .iter()
173                .flat_map(|v| v.iter().flat_map(|w| w.iter().cloned()))
174                .collect::<Vec<u8>>();
175            if !expected.is_empty() && !expected.ends_with(b"\r\n") {
176                expected.extend_from_slice(b"\r\n");
177            }
178            // println!("Read    : {:?}", show_bytes(&read));
179            // println!("Expected: {:?}", show_bytes(&expected));
180            assert_eq!(read, expected);
181        }
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    use quickcheck_macros::quickcheck;
190
191    #[quickcheck]
192    pub fn escaping_then_unescaping(
193        data: Vec<Vec<Vec<u8>>>,
194        maxread: usize,
195        initread: usize,
196        readlen: Vec<usize>,
197    ) {
198        fuzz::escaping_then_unescaping(data, maxread, initread, readlen)
199    }
200}